From 92436ffd23a12f400ee958a67a788edf2987a7bb Mon Sep 17 00:00:00 2001 From: Albert Llimos <53186777+albert-llimos@users.noreply.github.com> Date: Tue, 29 Oct 2024 13:54:16 +0100 Subject: [PATCH] Feat: Extend Evm Vault swaps functionality (#5344) * feat: witnessing btc smart contract swaps * chore: address minor review comments * chore: start implementation * test: fix deposit witnessing tests * chore: add intial scale encoding for cfParameters * chore: improve logic * fix: address RuntimeCall size limit * chore: address clippy * chore: more cleanup and refactoring * chore: add contract swaps to dca test * chore: engine refactor * chore: refactor createEvmWallet * chore: cleanup * chore: add MAX_VAULT_SWAP_ATTRIBUTES_LENGTH * chore: refactor bouncer * chore: refactor into common for reusal * chore: rename attributes to parameters * chore: pass extra parameters to sdk * chore: fix issues * chore: engine renaming * chore: update SDK with new encoding logic * chore: remove unnecessary bouncer ts-scale * chore: lint * chore: update to right name * chore: fix missing rename * chore: add beneficiares and make FoK mandatory * chore: lint * chore: update broker_fees and cli * chore: update with hardcoded cfParameters * chore: bump sdk * fix: simplify cf params decoding * chore: downgrade error -> warning * chore: bump sdk with new broker_fees type * chore: fix failing test * chore: bump sdk * chore: lint --------- Co-authored-by: Maxim Shishmarev Co-authored-by: Daniel --- bouncer/package.json | 2 +- bouncer/pnpm-lock.yaml | 18 +-- bouncer/shared/contract_swap.ts | 110 ++++++++++-------- bouncer/shared/new_swap.ts | 2 +- bouncer/shared/perform_swap.ts | 2 +- bouncer/shared/send.ts | 4 +- bouncer/shared/swap_context.ts | 11 +- bouncer/shared/swapping.ts | 16 +-- bouncer/shared/utils.ts | 30 ++++- bouncer/tests/DCA_test.ts | 99 +++++++++++----- bouncer/tests/evm_deposits.ts | 3 +- bouncer/tests/fill_or_kill.ts | 99 +++++++++++----- ...st_swap_deposit_address_with_affiliates.ts | 6 +- engine/src/witness/arb.rs | 21 ++-- engine/src/witness/common.rs | 1 + engine/src/witness/common/cf_parameters.rs | 46 ++++++++ engine/src/witness/eth.rs | 24 ++-- engine/src/witness/evm/vault.rs | 105 +++++++++++------ .../cf-integration-tests/src/solana.rs | 8 +- .../cf-integration-tests/src/swapping.rs | 4 +- state-chain/chains/src/arb/api.rs | 2 +- state-chain/chains/src/btc/api.rs | 2 +- state-chain/chains/src/ccm_checker.rs | 58 ++++----- state-chain/chains/src/dot/api.rs | 2 +- state-chain/chains/src/eth/api.rs | 2 +- state-chain/chains/src/lib.rs | 8 +- state-chain/chains/src/sol/api.rs | 20 ++-- state-chain/chains/src/sol/sol_tx_core.rs | 2 +- .../cf-ingress-egress/src/benchmarking.rs | 2 +- .../pallets/cf-ingress-egress/src/lib.rs | 10 +- .../pallets/cf-ingress-egress/src/tests.rs | 14 +-- state-chain/pallets/cf-swapping/src/tests.rs | 2 +- .../pallets/cf-swapping/src/tests/ccm.rs | 2 +- state-chain/primitives/src/lib.rs | 17 ++- state-chain/traits/src/mocks/api_call.rs | 4 +- .../traits/src/mocks/egress_handler.rs | 6 +- 36 files changed, 487 insertions(+), 277 deletions(-) create mode 100644 engine/src/witness/common/cf_parameters.rs diff --git a/bouncer/package.json b/bouncer/package.json index d41a0d3f9c..0a0ff85f2b 100644 --- a/bouncer/package.json +++ b/bouncer/package.json @@ -6,7 +6,7 @@ "prettier:write": "prettier --write ." }, "dependencies": { - "@chainflip/cli": "1.6.3", + "@chainflip/cli": "1.8.0-cf-parameters-rename.5", "@chainflip/utils": "^0.4.0", "@coral-xyz/anchor": "^0.30.1", "@iarna/toml": "^2.2.5", diff --git a/bouncer/pnpm-lock.yaml b/bouncer/pnpm-lock.yaml index 1b3b56eaa3..1cdd1b8811 100644 --- a/bouncer/pnpm-lock.yaml +++ b/bouncer/pnpm-lock.yaml @@ -9,8 +9,8 @@ importers: .: dependencies: '@chainflip/cli': - specifier: 1.6.3 - version: 1.6.3(axios@1.7.2)(bufferutil@4.0.8)(ethers@6.13.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(typescript@5.5.3)(utf-8-validate@5.0.10) + specifier: 1.8.0-cf-parameters-rename.5 + version: 1.8.0-cf-parameters-rename.5(axios@1.7.2)(bufferutil@4.0.8)(ethers@6.13.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(typescript@5.5.3)(utf-8-validate@5.0.10) '@chainflip/utils': specifier: ^0.4.0 version: 0.4.0 @@ -145,8 +145,8 @@ packages: '@chainflip/bitcoin@1.1.1': resolution: {integrity: sha512-Jr6X/0QTSFYpTp23ZPhyioL6wL9x3Xpj6OGjadQKZyhobRN4BZkhLogv20HDYTJcRzVphzrTJArbEaNie04XVA==} - '@chainflip/cli@1.6.3': - resolution: {integrity: sha512-isbPdFbO0tmQ7+7OjWSyzKtuog7FOgvkeb7fIini88LHsTjTXf8UvoKGYncOgwvLVQIY+J7NDnSQQmKdBg7PUw==} + '@chainflip/cli@1.8.0-cf-parameters-rename.5': + resolution: {integrity: sha512-+/1U/8IMuLZcRMCekq76heU0gptGvWho7joJ/I3Gbd/4SNI/K1pLp5wtATtE+bcELZ1QrtvAt+bK8yM9WNrCYg==} hasBin: true peerDependencies: axios: ^1.x @@ -155,8 +155,8 @@ packages: '@chainflip/extrinsics@1.6.1': resolution: {integrity: sha512-sm3v2QguNW4/RiIZYHd+VRSxup2q4cVBY0VjbQJw6xYqhNCzZlt+Hl38Kni+TCHQu3hP/WgIF72yvvSs7/PBdg==} - '@chainflip/rpc@1.6.7': - resolution: {integrity: sha512-OIE2cqzDy9Oq9sHuxJKOtbjP438QwMFdc6tuWdAmjjPcHGATI0Ft/lIKeCutD/8uH/CumOzaOoBaT2pI6G78Gg==} + '@chainflip/rpc@1.6.9': + resolution: {integrity: sha512-FtaoZBey4kIFhWQY5mMMAGXIgzSpZCX0DTnXQqPEn9q5qvt6FoQLDQk9k2IYKOTiIP+XsLy9w/WPPiH7+3BpnQ==} '@chainflip/solana@1.0.2': resolution: {integrity: sha512-8M+z0yjVCZBMpdCTGuBRt/Ns9NBJCNFvnSOjDu/kyaBRvxdUvECJOIseL0ENLBd0J/YmO9TcrZ30M0po3a96BA==} @@ -3169,11 +3169,11 @@ snapshots: transitivePeerDependencies: - typescript - '@chainflip/cli@1.6.3(axios@1.7.2)(bufferutil@4.0.8)(ethers@6.13.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(typescript@5.5.3)(utf-8-validate@5.0.10)': + '@chainflip/cli@1.8.0-cf-parameters-rename.5(axios@1.7.2)(bufferutil@4.0.8)(ethers@6.13.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(typescript@5.5.3)(utf-8-validate@5.0.10)': dependencies: '@chainflip/bitcoin': 1.1.1(typescript@5.5.3) '@chainflip/extrinsics': 1.6.1 - '@chainflip/rpc': 1.6.7 + '@chainflip/rpc': 1.6.9 '@chainflip/solana': 1.0.2 '@chainflip/utils': 0.4.0 axios: 1.7.2 @@ -3189,7 +3189,7 @@ snapshots: '@chainflip/extrinsics@1.6.1': {} - '@chainflip/rpc@1.6.7': + '@chainflip/rpc@1.6.9': dependencies: '@chainflip/utils': 0.3.0 zod: 3.23.8 diff --git a/bouncer/shared/contract_swap.ts b/bouncer/shared/contract_swap.ts index fa3f70c94c..d3a0d80eb4 100644 --- a/bouncer/shared/contract_swap.ts +++ b/bouncer/shared/contract_swap.ts @@ -5,10 +5,10 @@ import { approveVault, Asset as SCAsset, Chains, - InternalAsset, Chain, } from '@chainflip/cli'; -import { HDNodeWallet, Wallet, getDefaultProvider } from 'ethers'; +import { HDNodeWallet } from 'ethers'; +import { randomBytes } from 'crypto'; import { observeBalanceIncrease, getContractAddress, @@ -16,33 +16,56 @@ import { amountToFineAmount, defaultAssetAmounts, chainFromAsset, - getEvmEndpoint, assetDecimals, stateChainAssetFromAsset, - chainGasAsset, + createEvmWalletAndFund, + newAddress, } from './utils'; import { getBalance } from './get_balance'; -import { CcmDepositMetadata } from '../shared/new_swap'; -import { send } from './send'; +import { CcmDepositMetadata, DcaParams, FillOrKillParamsX128 } from '../shared/new_swap'; import { SwapContext, SwapStatus } from './swap_context'; const erc20Assets: Asset[] = ['Flip', 'Usdc', 'Usdt', 'ArbUsdc']; export async function executeContractSwap( - srcAsset: Asset, + sourceAsset: Asset, destAsset: Asset, destAddress: string, - wallet: HDNodeWallet, messageMetadata?: CcmDepositMetadata, + amount?: string, + boostFeeBps?: number, + fillOrKillParams?: FillOrKillParamsX128, + dcaParams?: DcaParams, + wallet?: HDNodeWallet, ): ReturnType { - const srcChain = chainFromAsset(srcAsset); + const srcChain = chainFromAsset(sourceAsset); const destChain = chainFromAsset(destAsset); + const amountToSwap = amount ?? defaultAssetAmounts(sourceAsset); + + const refundAddress = await newAddress(sourceAsset, randomBytes(32).toString('hex')); + const fokParams = fillOrKillParams ?? { + retryDurationBlocks: 0, + refundAddress, + minPriceX128: '0', + }; + + const evmWallet = wallet ?? (await createEvmWalletAndFund(sourceAsset)); + + if (erc20Assets.includes(sourceAsset)) { + // Doing effectively infinite approvals to make sure it doesn't fail. + // eslint-disable-next-line @typescript-eslint/no-use-before-define + await approveTokenVault( + sourceAsset, + (BigInt(amountToFineAmount(amountToSwap, assetDecimals(sourceAsset))) * 100n).toString(), + evmWallet, + ); + } const networkOptions = { - signer: wallet, + signer: evmWallet, network: 'localnet', vaultContractAddress: getContractAddress(srcChain, 'VAULT'), - srcTokenContractAddress: getContractAddress(srcChain, srcAsset), + srcTokenContractAddress: getContractAddress(srcChain, sourceAsset), } as const; const txOptions = { // This is run with fresh addresses to prevent nonce issues. Will be 1 for ERC20s. @@ -55,15 +78,21 @@ export async function executeContractSwap( destAsset: stateChainAssetFromAsset(destAsset), // It is important that this is large enough to result in // an amount larger than existential (e.g. on Polkadot): - amount: amountToFineAmount(defaultAssetAmounts(srcAsset), assetDecimals(srcAsset)), + amount: amountToFineAmount(amountToSwap, assetDecimals(sourceAsset)), destAddress, - srcAsset: stateChainAssetFromAsset(srcAsset), + srcAsset: stateChainAssetFromAsset(sourceAsset), srcChain, ccmParams: messageMetadata && { gasBudget: messageMetadata.gasBudget.toString(), message: messageMetadata.message, - cfParameters: messageMetadata.cfParameters, + ccmAdditionalData: messageMetadata.ccmAdditionalData, }, + // The SDK will encode these parameters and the ccmAdditionalData + // into the `cfParameters` field for the vault swap. + boostFeeBps, + fillOrKillParams: fokParams, + dcaParams, + beneficiaries: undefined, } as ExecuteSwapParams, networkOptions, txOptions, @@ -86,37 +115,18 @@ export async function performSwapViaContract( messageMetadata?: CcmDepositMetadata, swapContext?: SwapContext, log = true, + amount?: string, + boostFeeBps?: number, + fillOrKillParams?: FillOrKillParamsX128, + dcaParams?: DcaParams, ): Promise { const tag = swapTag ?? ''; - - const srcChain = chainFromAsset(sourceAsset); - - // Generate a new wallet for each contract swap to prevent nonce issues when running in parallel - // with other swaps via deposit channels. - const mnemonic = Wallet.createRandom().mnemonic?.phrase ?? ''; - if (mnemonic === '') { - throw new Error('Failed to create random mnemonic'); - } - const wallet = Wallet.fromPhrase(mnemonic).connect(getDefaultProvider(getEvmEndpoint(srcChain))); + const amountToSwap = amount ?? defaultAssetAmounts(sourceAsset); try { - // Fund new key with native asset and asset to swap. - await send(chainGasAsset(srcChain) as InternalAsset, wallet.address); - await send(sourceAsset, wallet.address); - - if (erc20Assets.includes(sourceAsset)) { - // Doing effectively infinite approvals to make sure it doesn't fail. - // eslint-disable-next-line @typescript-eslint/no-use-before-define - await approveTokenVault( - sourceAsset, - ( - BigInt(amountToFineAmount(defaultAssetAmounts(sourceAsset), assetDecimals(sourceAsset))) * - 100n - ).toString(), - wallet, - ); - } - swapContext?.updateStatus(swapTag, SwapStatus.ContractApproved); + // Generate a new wallet for each contract swap to prevent nonce issues when running in parallel + // with other swaps via deposit channels. + const wallet = await createEvmWalletAndFund(sourceAsset); const oldBalance = await getBalance(destAsset, destAddress); if (log) { @@ -133,8 +143,12 @@ export async function performSwapViaContract( sourceAsset, destAsset, destAddress, - wallet, messageMetadata, + amountToSwap, + boostFeeBps, + fillOrKillParams, + dcaParams, + wallet, ); swapContext?.updateStatus(swapTag, SwapStatus.ContractExecuted); @@ -171,24 +185,24 @@ export async function performSwapViaContract( throw new Error(`${tag} ${err}`); } } -export async function approveTokenVault(srcAsset: Asset, amount: string, wallet: HDNodeWallet) { - if (!erc20Assets.includes(srcAsset)) { - throw new Error(`Unsupported asset, not an ERC20: ${srcAsset}`); +export async function approveTokenVault(sourceAsset: Asset, amount: string, wallet: HDNodeWallet) { + if (!erc20Assets.includes(sourceAsset)) { + throw new Error(`Unsupported asset, not an ERC20: ${sourceAsset}`); } - const chain = chainFromAsset(srcAsset as Asset); + const chain = chainFromAsset(sourceAsset as Asset); await approveVault( { amount, srcChain: chain as Chain, - srcAsset: stateChainAssetFromAsset(srcAsset) as SCAsset, + srcAsset: stateChainAssetFromAsset(sourceAsset) as SCAsset, }, { signer: wallet, network: 'localnet', vaultContractAddress: getContractAddress(chain, 'VAULT'), - srcTokenContractAddress: getContractAddress(chain, srcAsset), + srcTokenContractAddress: getContractAddress(chain, sourceAsset), }, // This is run with fresh addresses to prevent nonce issues { diff --git a/bouncer/shared/new_swap.ts b/bouncer/shared/new_swap.ts index 69453842c2..0919ddd0a4 100644 --- a/bouncer/shared/new_swap.ts +++ b/bouncer/shared/new_swap.ts @@ -38,7 +38,7 @@ export async function newSwap( ccmParams: messageMetadata && { message: messageMetadata.message as `0x${string}`, gasBudget: messageMetadata.gasBudget.toString(), - cfParameters: messageMetadata.cfParameters as `0x${string}`, + ccmAdditionalData: messageMetadata.ccmAdditionalData as `0x${string}`, }, commissionBps: brokerCommissionBps, maxBoostFeeBps: boostFeeBps, diff --git a/bouncer/shared/perform_swap.ts b/bouncer/shared/perform_swap.ts index 45814b70f2..465b84805e 100644 --- a/bouncer/shared/perform_swap.ts +++ b/bouncer/shared/perform_swap.ts @@ -70,7 +70,7 @@ export async function requestNewSwap( ? event.data.channelMetadata !== null && event.data.channelMetadata.message === messageMetadata.message && event.data.channelMetadata.gasBudget.replace(/,/g, '') === messageMetadata.gasBudget && - event.data.channelMetadata.cfParameters === messageMetadata.cfParameters + event.data.channelMetadata.ccmAdditionalData === messageMetadata.ccmAdditionalData : event.data.channelMetadata === null; return destAddressMatches && destAssetMatches && sourceAssetMatches && ccmMetadataMatches; diff --git a/bouncer/shared/send.ts b/bouncer/shared/send.ts index 984806351f..521f66fc34 100644 --- a/bouncer/shared/send.ts +++ b/bouncer/shared/send.ts @@ -20,9 +20,7 @@ import { sendSolUsdc } from './send_solusdc'; const cfTesterAbi = await getCFTesterAbi(); export async function send(asset: Asset, address: string, amount?: string, log = true) { - // TODO: Remove this any when we have Sol assets in the Asset type. - // eslint-disable-next-line @typescript-eslint/no-explicit-any - switch (asset as any) { + switch (asset) { case 'Btc': await sendBtc(address, amount ?? defaultAssetAmounts(asset)); break; diff --git a/bouncer/shared/swap_context.ts b/bouncer/shared/swap_context.ts index 6c9ff44286..2d2a6bc681 100644 --- a/bouncer/shared/swap_context.ts +++ b/bouncer/shared/swap_context.ts @@ -3,8 +3,6 @@ import assert from 'assert'; export enum SwapStatus { Initiated, Funded, - // Contract swap specific statuses - ContractApproved, ContractExecuted, SwapScheduled, Success, @@ -37,16 +35,9 @@ export class SwapContext { ); break; } - case SwapStatus.ContractApproved: { - assert( - currentStatus === SwapStatus.Initiated, - `Unexpected status transition for ${tag}. Transitioning from ${currentStatus} to ${status}`, - ); - break; - } case SwapStatus.ContractExecuted: { assert( - currentStatus === SwapStatus.ContractApproved, + currentStatus === SwapStatus.Initiated, `Unexpected status transition for ${tag}. Transitioning from ${currentStatus} to ${status}`, ); break; diff --git a/bouncer/shared/swapping.ts b/bouncer/shared/swapping.ts index 5642c4ea69..0bde37e88d 100644 --- a/bouncer/shared/swapping.ts +++ b/bouncer/shared/swapping.ts @@ -12,7 +12,7 @@ import { defaultAssetAmounts, ccmSupportedChains, assetDecimals, - solCfParamsCodec, + solCcmAdditionalDataCodec, } from '../shared/utils'; import { BtcAddressType } from '../shared/new_btc_address'; import { CcmDepositMetadata } from '../shared/new_swap'; @@ -69,7 +69,7 @@ function newAbiEncodedMessage(types?: SolidityType[]): string { return web3.eth.abi.encodeParameters(typesArray, variables); } -export function newSolanaCfParameters(maxAccounts: number) { +export function newSolanaCcmAdditionalData(maxAccounts: number) { const cfReceiverAddress = getContractAddress('Solana', 'CFTESTER'); const fallbackAddress = Keypair.generate().publicKey.toBytes(); @@ -93,7 +93,7 @@ export function newSolanaCfParameters(maxAccounts: number) { fallback_address: fallbackAddress, }; - return u8aToHex(solCfParamsCodec.enc(cfParameters)); + return u8aToHex(solCcmAdditionalDataCodec.enc(cfParameters)); } // Solana CCM-related parameters. These are values in the protocol. @@ -107,7 +107,7 @@ function newCcmArbitraryBytes(maxLength: number): string { return randomAsHex(Math.floor(Math.random() * Math.max(0, maxLength - 10)) + 10); } -function newCfParameters(destAsset: Asset, message?: string): string { +function newCcmAdditionalData(destAsset: Asset, message?: string): string { const destChain = chainFromAsset(destAsset); switch (destChain) { case 'Ethereum': @@ -123,7 +123,7 @@ function newCfParameters(destAsset: Asset, message?: string): string { // The maximum number of extra accounts that can be passed is limited by the tx size // and therefore also depends on the message length. - return newSolanaCfParameters(maxAccounts); + return newSolanaCcmAdditionalData(maxAccounts); } default: throw new Error(`Unsupported chain: ${destChain}`); @@ -151,7 +151,7 @@ export function newCcmMetadata( cfParamsArray?: string, ): CcmDepositMetadata { const message = ccmMessage ?? newCcmMessage(destAsset); - const cfParameters = cfParamsArray ?? newCfParameters(destAsset, message); + const ccmAdditionalData = cfParamsArray ?? newCcmAdditionalData(destAsset, message); const gasDiv = gasBudgetFraction ?? 2; const gasBudget = Math.floor( @@ -162,7 +162,7 @@ export function newCcmMetadata( return { message, gasBudget, - cfParameters, + ccmAdditionalData, }; } @@ -250,7 +250,7 @@ export async function testSwapViaContract( destAsset, addressType, messageMetadata, - (tagSuffix ?? '') + ' Contract', + (tagSuffix ?? '') + 'Contract', log, swapContext, ); diff --git a/bouncer/shared/utils.ts b/bouncer/shared/utils.ts index 759c2744b8..073626d3af 100644 --- a/bouncer/shared/utils.ts +++ b/bouncer/shared/utils.ts @@ -1,5 +1,6 @@ import { execSync } from 'child_process'; import * as crypto from 'crypto'; +import { HDNodeWallet, Wallet, getDefaultProvider } from 'ethers'; import { setTimeout as sleep } from 'timers/promises'; import Client from 'bitcoin-core'; import { ApiPromise, Keyring } from '@polkadot/api'; @@ -31,6 +32,7 @@ import { SwapParams } from './perform_swap'; import { newSolAddress } from './new_sol_address'; import { getChainflipApi, observeBadEvent, observeEvent } from './utils/substrate'; import { execWithLog } from './utils/exec_with_log'; +import { send } from './send'; const cfTesterAbi = await getCFTesterAbi(); const cfTesterIdl = await getCfTesterIdl(); @@ -52,7 +54,7 @@ const isSDKChain = (chain: Chain): chain is SDKChain => chain in chainConstants; export const solanaNumberOfNonces = 10; -export const solCfParamsCodec = Struct({ +export const solCcmAdditionalDataCodec = Struct({ cf_receiver: Struct({ pubkey: TsBytes(32), is_writable: bool, @@ -484,7 +486,7 @@ function checkRequestTypeMatches(actual: object | string, expected: SwapRequestT export async function observeSwapRequested( sourceAsset: Asset, destAsset: Asset, - channelId: number, + id: number | string, swapRequestType: SwapRequestType, ) { // need to await this to prevent the chainflip api from being disposed prematurely @@ -492,9 +494,12 @@ export async function observeSwapRequested( test: (event) => { const data = event.data; - if (typeof data.origin === 'object' && 'DepositChannel' in data.origin) { + if (typeof data.origin === 'object') { const channelMatches = - Number(data.origin.DepositChannel.channelId.replaceAll(',', '')) === channelId; + (typeof id === 'number' && + 'DepositChannel' in data.origin && + Number(data.origin.DepositChannel.channelId.replaceAll(',', '')) === id) || + (typeof id === 'string' && 'Vault' in data.origin && data.origin.Vault.txHash === id); const sourceAssetMatches = sourceAsset === (data.inputAsset as Asset); const destAssetMatches = destAsset === (data.outputAsset as Asset); const requestTypeMatches = checkRequestTypeMatches(data.requestType, swapRequestType); @@ -746,8 +751,8 @@ export async function observeSolanaCcmEvent( // The message is being used as the main discriminator if (matchEventName && matchSourceChain && matchMessage) { - const { remaining_accounts: expectedRemainingAccounts } = solCfParamsCodec.dec( - messageMetadata.cfParameters!, + const { remaining_accounts: expectedRemainingAccounts } = solCcmAdditionalDataCodec.dec( + messageMetadata.ccmAdditionalData!, ); if ( @@ -1152,3 +1157,16 @@ export function getTimeStamp(): string { const seconds = now.getSeconds().toString().padStart(2, '0'); return `${hours}:${minutes}:${seconds}`; } + +export async function createEvmWalletAndFund(asset: Asset): Promise { + const chain = chainFromAsset(asset); + + const mnemonic = Wallet.createRandom().mnemonic?.phrase ?? ''; + if (mnemonic === '') { + throw new Error('Failed to create random mnemonic'); + } + const wallet = Wallet.fromPhrase(mnemonic).connect(getDefaultProvider(getEvmEndpoint(chain))); + await send(chainGasAsset(chain) as SDKAsset, wallet.address, undefined, false); + await send(asset, wallet.address, undefined, false); + return wallet; +} diff --git a/bouncer/tests/DCA_test.ts b/bouncer/tests/DCA_test.ts index 43e14aad59..725305d906 100644 --- a/bouncer/tests/DCA_test.ts +++ b/bouncer/tests/DCA_test.ts @@ -12,7 +12,8 @@ import { observeEvent, observeEvents } from '../shared/utils/substrate'; import { getBalance } from '../shared/get_balance'; import { ExecutableTest } from '../shared/executable_test'; import { requestNewSwap } from '../shared/perform_swap'; -import { DcaParams } from '../shared/new_swap'; +import { DcaParams, FillOrKillParamsX128 } from '../shared/new_swap'; +import { executeContractSwap } from '../shared/contract_swap'; /* eslint-disable @typescript-eslint/no-use-before-define */ export const testDCASwaps = new ExecutableTest('DCA-Swaps', main, 150); @@ -20,50 +21,85 @@ export const testDCASwaps = new ExecutableTest('DCA-Swaps', main, 150); // Requested number of blocks between each chunk const CHUNK_INTERVAL = 2; -async function testDCASwap(inputAsset: Asset, amount: number, numberOfChunks: number) { +async function testDCASwap( + inputAsset: Asset, + amount: number, + numberOfChunks: number, + swapviaContract = false, +) { assert(numberOfChunks > 1, 'Number of chunks must be greater than 1'); - const dcaParameters: DcaParams = { + const dcaParams: DcaParams = { numberOfChunks, chunkIntervalBlocks: CHUNK_INTERVAL, }; + const fillOrKillParams: FillOrKillParamsX128 = { + refundAddress: '0xa56A6be23b6Cf39D9448FF6e897C29c41c8fbDFF', + minPriceX128: '1', + retryDurationBlocks: 100, + }; const destAsset = inputAsset === Assets.Usdc ? Assets.Flip : Assets.Usdc; + const destAddress = await newAddress(destAsset, randomBytes(32).toString('hex')); + const destBalanceBefore = await getBalance(inputAsset, destAddress); testDCASwaps.debugLog(`DCA destination address: ${destAddress}`); - const swapRequest = await requestNewSwap( - inputAsset, - destAsset, - destAddress, - 'DCA_Test', - undefined, // messageMetadata - 0, // brokerCommissionBps - false, // log - 0, // boostFeeBps - { - refundAddress: '0xa56A6be23b6Cf39D9448FF6e897C29c41c8fbDFF', - minPriceX128: '1', - retryDurationBlocks: 100, - }, // FoK parameters - dcaParameters, - ); + let swapRequestedHandle; - const depositChannelId = swapRequest.channelId; - const swapRequestedHandle = observeSwapRequested( - inputAsset, - destAsset, - depositChannelId, - SwapRequestType.Regular, - ); + if (!swapviaContract) { + const swapRequest = await requestNewSwap( + inputAsset, + destAsset, + destAddress, + 'DCA_Test', + undefined, // messageMetadata + 0, // brokerCommissionBps + false, // log + 0, // boostFeeBps + fillOrKillParams, + dcaParams, + ); - // Deposit the asset - await send(inputAsset, swapRequest.depositAddress, amount.toString()); - testDCASwaps.log(`Sent ${amount} ${inputAsset} to ${swapRequest.depositAddress}`); + const depositChannelId = swapRequest.channelId; + swapRequestedHandle = observeSwapRequested( + inputAsset, + destAsset, + depositChannelId, + SwapRequestType.Regular, + ); + + // Deposit the asset + await send(inputAsset, swapRequest.depositAddress, amount.toString()); + testDCASwaps.log(`Sent ${amount} ${inputAsset} to ${swapRequest.depositAddress}`); + } else { + const receipt = await executeContractSwap( + inputAsset, + destAsset, + destAddress, + undefined, + amount.toString(), + undefined, + fillOrKillParams, + dcaParams, + ); + + testDCASwaps.log(`Contract swap executed, tx hash: ${receipt.hash}`); + + // Look after Swap Requested of data.origin.Vault.tx_hash + swapRequestedHandle = observeSwapRequested( + inputAsset, + destAsset, + receipt.hash, + SwapRequestType.Regular, + ); + } const swapRequestId = Number((await swapRequestedHandle).data.swapRequestId.replaceAll(',', '')); - testDCASwaps.debugLog(`${inputAsset} swap requested, swapRequestId: ${swapRequestId}`); + testDCASwaps.debugLog( + `${inputAsset} swap ${swapviaContract ? 'via contract' : ''}, swapRequestId: ${swapRequestId}`, + ); // Wait for the swap to complete await observeEvent(`swapping:SwapRequestCompleted`, { @@ -92,11 +128,12 @@ async function testDCASwap(inputAsset: Asset, amount: number, numberOfChunks: nu `Unexpected chunk interval between chunk ${i - 1} & ${i}`, ); } + testDCASwaps.log(`Chunk interval of ${CHUNK_INTERVAL} verified for all ${numberOfChunks} chunks`); await observeBalanceIncrease(destAsset, destAddress, destBalanceBefore); } export async function main() { - await testDCASwap(Assets.Eth, 1, 2); + await Promise.all([testDCASwap(Assets.Eth, 1, 2), testDCASwap(Assets.ArbEth, 1, 2, true)]); } diff --git a/bouncer/tests/evm_deposits.ts b/bouncer/tests/evm_deposits.ts index 73638b5f7a..02a293e5d1 100644 --- a/bouncer/tests/evm_deposits.ts +++ b/bouncer/tests/evm_deposits.ts @@ -97,7 +97,8 @@ async function testTxMultipleContractSwaps(sourceAsset: Asset, destAsset: Asset) assetContractId(destAsset), getContractAddress(chainFromAsset(sourceAsset), sourceAsset), amount, - '0x', + // Encoded EVM refund address and no other swap parameters. + '0x0000000000000e879c89cad7076b347bde13c99cf2c33e7299b60000000000000000000000000000000000000000000000000000000000000000000000', numSwaps, ) .encodeABI(); diff --git a/bouncer/tests/fill_or_kill.ts b/bouncer/tests/fill_or_kill.ts index bbdf7a6e3e..591cba52ca 100644 --- a/bouncer/tests/fill_or_kill.ts +++ b/bouncer/tests/fill_or_kill.ts @@ -13,18 +13,21 @@ import { requestNewSwap } from '../shared/perform_swap'; import { send } from '../shared/send'; import { getBalance } from '../shared/get_balance'; import { observeEvent } from '../shared/utils/substrate'; -import { FillOrKillParamsX128 } from '../shared/new_swap'; +import { CcmDepositMetadata, FillOrKillParamsX128 } from '../shared/new_swap'; import { ExecutableTest } from '../shared/executable_test'; +import { executeContractSwap } from '../shared/contract_swap'; +import { newCcmMetadata } from '../shared/swapping'; /* eslint-disable @typescript-eslint/no-use-before-define */ export const testFillOrKill = new ExecutableTest('FoK', main, 600); /// Do a swap with unrealistic minimum price so it gets refunded. -async function testMinPriceRefund(inputAsset: Asset, amount: number) { +async function testMinPriceRefund(inputAsset: Asset, amount: number, swapviaContract = false) { const destAsset = inputAsset === Assets.Usdc ? Assets.Flip : Assets.Usdc; const refundAddress = await newAddress(inputAsset, randomBytes(32).toString('hex')); const destAddress = await newAddress(destAsset, randomBytes(32).toString('hex')); testFillOrKill.debugLog(`Swap destination address: ${destAddress}`); + testFillOrKill.debugLog(`Refund address: ${refundAddress}`); const refundBalanceBefore = await getBalance(inputAsset, refundAddress); @@ -39,33 +42,66 @@ async function testMinPriceRefund(inputAsset: Asset, amount: number) { ), }; - testFillOrKill.log( - `Requesting swap from ${inputAsset} to ${destAsset} with unrealistic min price`, - ); - const swapRequest = await requestNewSwap( - inputAsset, - destAsset, - destAddress, - 'FoK_Test', - undefined, // messageMetadata - 0, // brokerCommissionBps - false, // log - 0, // boostFeeBps - refundParameters, - ); - const depositAddress = swapRequest.depositAddress; - const depositChannelId = swapRequest.channelId; - - const swapRequestedHandle = observeSwapRequested( - inputAsset, - destAsset, - depositChannelId, - SwapRequestType.Regular, - ); - - // Deposit the asset - await send(inputAsset, depositAddress, amount.toString()); - testFillOrKill.log(`Sent ${amount} ${inputAsset} to ${depositAddress}`); + let swapRequestedHandle; + + if (!swapviaContract) { + testFillOrKill.log( + `Requesting swap from ${inputAsset} to ${destAsset} with unrealistic min price`, + ); + const swapRequest = await requestNewSwap( + inputAsset, + destAsset, + destAddress, + 'FoK_Test', + undefined, // messageMetadata + 0, // brokerCommissionBps + false, // log + 0, // boostFeeBps + refundParameters, + ); + const depositAddress = swapRequest.depositAddress; + const depositChannelId = swapRequest.channelId; + + swapRequestedHandle = observeSwapRequested( + inputAsset, + destAsset, + depositChannelId, + SwapRequestType.Regular, + ); + + // Deposit the asset + await send(inputAsset, depositAddress, amount.toString()); + testFillOrKill.log(`Sent ${amount} ${inputAsset} to ${depositAddress}`); + } else { + testFillOrKill.log( + `Swapping via contract from ${inputAsset} to ${destAsset} with unrealistic min price`, + ); + + // Randomly use CCM to test different encodings + let ccmMetadata: CcmDepositMetadata | undefined; + if (Math.random() < 0.5) { + ccmMetadata = newCcmMetadata(inputAsset, destAsset, undefined, 100); + ccmMetadata.ccmAdditionalData = + Math.random() < 0.5 ? ccmMetadata.ccmAdditionalData : undefined; + } + + const receipt = await executeContractSwap( + inputAsset, + destAsset, + destAddress, + undefined, + amount.toString(), + undefined, + refundParameters, + ); + + swapRequestedHandle = observeSwapRequested( + inputAsset, + destAsset, + receipt.hash, + SwapRequestType.Regular, + ); + } const swapRequestedEvent = await swapRequestedHandle; const swapRequestId = Number(swapRequestedEvent.data.swapRequestId.replaceAll(',', '')); @@ -87,8 +123,6 @@ async function testMinPriceRefund(inputAsset: Asset, amount: number) { `${inputAsset} swap ${swapRequestId} was executed instead of failing and being refunded`, ); } - - testFillOrKill.log(`FoK ${inputAsset} swap refunded`); } async function main() { @@ -98,5 +132,8 @@ async function main() { testMinPriceRefund(Assets.Dot, 100), testMinPriceRefund(Assets.Btc, 0.1), testMinPriceRefund(Assets.Usdc, 1000), + testMinPriceRefund(Assets.Flip, 500, true), + testMinPriceRefund(Assets.Eth, 1, true), + testMinPriceRefund(Assets.ArbEth, 5, true), ]); } diff --git a/bouncer/tests/request_swap_deposit_address_with_affiliates.ts b/bouncer/tests/request_swap_deposit_address_with_affiliates.ts index 794468dacc..d90a905803 100644 --- a/bouncer/tests/request_swap_deposit_address_with_affiliates.ts +++ b/bouncer/tests/request_swap_deposit_address_with_affiliates.ts @@ -40,7 +40,7 @@ const eventSchema = z destinationAsset: z.string(), brokerCommissionRate: numberSchema, channelMetadata: z - .object({ message: z.string(), gasBudget: bigintSchema, cfParameters: z.string() }) + .object({ message: z.string(), gasBudget: bigintSchema, ccmAdditionalData: z.string() }) .nullable(), boostFee: numberSchema, affiliateFees: z.array(z.object({ account: z.string(), bps: numberSchema })), @@ -141,7 +141,7 @@ const requestSwapDepositAddress = async ( if (params.ccmParams) { assert.strictEqual(event.channelMetadata?.message, params.ccmParams.message); assert.strictEqual(event.channelMetadata.gasBudget, BigInt(params.ccmParams.gasBudget)); - assert.strictEqual(event.channelMetadata.cfParameters, params.ccmParams.cfParameters); + assert.strictEqual(event.channelMetadata.ccmAdditionalData, params.ccmParams.ccmAdditionalData); } }; @@ -215,7 +215,7 @@ const withCcm: NewSwapRequest = { ccmParams: { message: '0xcafebabe', gasBudget: '1000000', - cfParameters: '0xdeadbeef', + ccmAdditionalData: '0xdeadbeef', }, }; diff --git a/engine/src/witness/arb.rs b/engine/src/witness/arb.rs index d38886b7b6..1fe39cf897 100644 --- a/engine/src/witness/arb.rs +++ b/engine/src/witness/arb.rs @@ -18,7 +18,7 @@ use crate::{ stream_api::{StreamApi, FINALIZED}, STATE_CHAIN_CONNECTION, }, - witness::evm::erc20_deposits::usdc::UsdcEvents, + witness::{common::cf_parameters::ShortId, evm::erc20_deposits::usdc::UsdcEvents}, }; use super::{ @@ -175,8 +175,10 @@ where struct ArbCallBuilder {} -use cf_chains::{address::EncodedAddress, CcmDepositMetadata}; -use cf_primitives::{Asset, AssetAmount, TransactionHash}; +use cf_chains::{address::EncodedAddress, CcmDepositMetadata, ChannelRefundParameters}; +use cf_primitives::{ + Asset, AssetAmount, BasisPoints, Beneficiaries, DcaParameters, TransactionHash, +}; impl super::evm::vault::IngressCallBuilder for ArbCallBuilder { type Chain = Arbitrum; @@ -188,6 +190,11 @@ impl super::evm::vault::IngressCallBuilder for ArbCallBuilder { destination_address: EncodedAddress, deposit_metadata: Option, tx_hash: TransactionHash, + _broker_fees: Beneficiaries, + refund_params: Option, + dca_params: Option, + // This is only to be checked in the pre-witnessed version + boost_fee: Option, ) -> state_chain_runtime::RuntimeCall { state_chain_runtime::RuntimeCall::ArbitrumIngressEgress( pallet_cf_ingress_egress::Call::contract_swap_request { @@ -198,11 +205,11 @@ impl super::evm::vault::IngressCallBuilder for ArbCallBuilder { deposit_metadata, tx_hash, deposit_details: Box::new(DepositDetails { tx_hashes: Some(vec![tx_hash.into()]) }), + // Defaulting to no broker fees until PRO-1743 is completed. broker_fees: Default::default(), - // TODO: use real parameters when we can decode them - boost_fee: 0, - dca_params: None, - refund_params: None, + boost_fee: boost_fee.unwrap_or_default(), + dca_params, + refund_params: refund_params.map(Box::new), }, ) } diff --git a/engine/src/witness/common.rs b/engine/src/witness/common.rs index 60690923d6..56b2ba4857 100644 --- a/engine/src/witness/common.rs +++ b/engine/src/witness/common.rs @@ -1,3 +1,4 @@ +pub mod cf_parameters; pub mod chain_source; pub mod chunked_chain_source; pub mod epoch_source; diff --git a/engine/src/witness/common/cf_parameters.rs b/engine/src/witness/common/cf_parameters.rs new file mode 100644 index 0000000000..e84d88812f --- /dev/null +++ b/engine/src/witness/common/cf_parameters.rs @@ -0,0 +1,46 @@ +use cf_chains::{CcmAdditionalData, ChannelRefundParameters}; +use cf_primitives::{BasisPoints, Beneficiaries, DcaParameters}; +use codec::{Decode, Encode, MaxEncodedLen}; +use scale_info::TypeInfo; + +#[derive(Encode, Decode, MaxEncodedLen, TypeInfo, Clone, PartialEq, Debug)] +pub struct CfParameters { + /// CCMs may require additional data (for example CCMs to Solana require adding a list of + /// addresses). + pub ccm_additional_data: CcmData, + pub vault_swap_parameters: VaultSwapParameters, +} + +pub type CcmCfParameters = CfParameters; + +// TODO: Define this / implement it on the SC - PRO-1743. +pub type ShortId = u8; + +#[derive(Encode, Decode, MaxEncodedLen, TypeInfo, Clone, PartialEq, Debug)] +pub struct VaultSwapParameters { + pub refund_params: ChannelRefundParameters, + pub dca_params: Option, + pub boost_fee: Option, + pub broker_fees: Beneficiaries, +} + +#[cfg(test)] +mod tests { + use super::*; + use cf_chains::MAX_CCM_ADDITIONAL_DATA_LENGTH; + + const MAX_VAULT_SWAP_PARAMETERS_LENGTH: u32 = 1_000; + const MAX_CF_PARAM_LENGTH: u32 = + MAX_CCM_ADDITIONAL_DATA_LENGTH + MAX_VAULT_SWAP_PARAMETERS_LENGTH; + + #[test] + fn test_cf_parameters_max_length() { + assert!( + MAX_VAULT_SWAP_PARAMETERS_LENGTH as usize >= VaultSwapParameters::max_encoded_len() + ); + assert!(MAX_CF_PARAM_LENGTH as usize >= CfParameters::<()>::max_encoded_len()); + assert!( + MAX_VAULT_SWAP_PARAMETERS_LENGTH as usize >= VaultSwapParameters::max_encoded_len() + ); + } +} diff --git a/engine/src/witness/eth.rs b/engine/src/witness/eth.rs index f61b6c2642..d8e8c044ff 100644 --- a/engine/src/witness/eth.rs +++ b/engine/src/witness/eth.rs @@ -19,7 +19,10 @@ use crate::{ stream_api::{StreamApi, FINALIZED}, STATE_CHAIN_CONNECTION, }, - witness::evm::erc20_deposits::{flip::FlipEvents, usdc::UsdcEvents, usdt::UsdtEvents}, + witness::{ + common::cf_parameters::ShortId, + evm::erc20_deposits::{flip::FlipEvents, usdc::UsdcEvents, usdt::UsdtEvents}, + }, }; use super::{common::epoch_source::EpochSourceBuilder, evm::source::EvmSource}; @@ -218,8 +221,10 @@ where Ok(()) } -use cf_chains::{address::EncodedAddress, CcmDepositMetadata}; -use cf_primitives::{Asset, AssetAmount, TransactionHash}; +use cf_chains::{address::EncodedAddress, CcmDepositMetadata, ChannelRefundParameters}; +use cf_primitives::{ + Asset, AssetAmount, BasisPoints, Beneficiaries, DcaParameters, TransactionHash, +}; pub struct EthCallBuilder {} @@ -233,6 +238,11 @@ impl super::evm::vault::IngressCallBuilder for EthCallBuilder { destination_address: EncodedAddress, deposit_metadata: Option, tx_hash: TransactionHash, + _broker_fees: Beneficiaries, + refund_params: Option, + dca_params: Option, + // This is only to be checked in the pre-witnessed version + boost_fee: Option, ) -> state_chain_runtime::RuntimeCall { state_chain_runtime::RuntimeCall::EthereumIngressEgress( pallet_cf_ingress_egress::Call::contract_swap_request { @@ -243,11 +253,11 @@ impl super::evm::vault::IngressCallBuilder for EthCallBuilder { deposit_metadata, tx_hash, deposit_details: Box::new(DepositDetails { tx_hashes: Some(vec![tx_hash.into()]) }), + // Defaulting to no broker fees until PRO-1743 is completed. broker_fees: Default::default(), - // TODO: use real parameters when we can decode them - boost_fee: 0, - dca_params: None, - refund_params: None, + boost_fee: boost_fee.unwrap_or_default(), + dca_params, + refund_params: refund_params.map(Box::new), }, ) } diff --git a/engine/src/witness/evm/vault.rs b/engine/src/witness/evm/vault.rs index 206679638d..543c138ccd 100644 --- a/engine/src/witness/evm/vault.rs +++ b/engine/src/witness/evm/vault.rs @@ -1,11 +1,12 @@ +use crate::evm::retry_rpc::EvmRetryRpcApi; +use codec::Decode; use ethers::types::Bloom; use sp_core::H256; use std::collections::HashMap; -use crate::evm::retry_rpc::EvmRetryRpcApi; - use super::{ super::common::{ + cf_parameters::*, chain_source::ChainClient, chunked_chain_source::chunked_by_vault::{builder::ChunkedByVaultBuilder, ChunkedByVault}, }, @@ -19,9 +20,9 @@ use cf_chains::{ address::{EncodedAddress, IntoForeignChainAddress}, eth::Address as EthereumAddress, evm::DepositDetails, - CcmChannelMetadata, CcmDepositMetadata, Chain, + CcmChannelMetadata, CcmDepositMetadata, Chain, ChannelRefundParameters, }; -use cf_primitives::{Asset, ForeignChain}; +use cf_primitives::{Asset, BasisPoints, DcaParameters, ForeignChain}; use ethers::prelude::*; use state_chain_runtime::{EthereumInstance, Runtime, RuntimeCall}; @@ -63,15 +64,24 @@ where dst_token, amount, sender: _, - cf_parameters: _, - }) => Some(CallBuilder::contract_swap_request( - native_asset, - try_into_primitive(amount)?, - try_into_primitive(dst_token)?, - try_into_encoded_address(try_into_primitive(dst_chain)?, dst_address.to_vec())?, - None, - event.tx_hash.into(), - )), + cf_parameters, + }) => { + let CfParameters { ccm_additional_data: (), vault_swap_parameters } = + CfParameters::decode(&mut &cf_parameters[..])?; + + Some(CallBuilder::contract_swap_request( + native_asset, + try_into_primitive(amount)?, + try_into_primitive(dst_token)?, + try_into_encoded_address(try_into_primitive(dst_chain)?, dst_address.to_vec())?, + None, + event.tx_hash.into(), + vault_swap_parameters.broker_fees, + Some(vault_swap_parameters.refund_params), + vault_swap_parameters.dca_params, + vault_swap_parameters.boost_fee, + )) + }, VaultEvents::SwapTokenFilter(SwapTokenFilter { dst_chain, dst_address, @@ -79,17 +89,26 @@ where src_token, amount, sender: _, - cf_parameters: _, - }) => Some(CallBuilder::contract_swap_request( - *(supported_assets - .get(&src_token) - .ok_or(anyhow!("Source token {src_token:?} not found"))?), - try_into_primitive(amount)?, - try_into_primitive(dst_token)?, - try_into_encoded_address(try_into_primitive(dst_chain)?, dst_address.to_vec())?, - None, - event.tx_hash.into(), - )), + cf_parameters, + }) => { + let CfParameters { ccm_additional_data: (), vault_swap_parameters } = + CfParameters::decode(&mut &cf_parameters[..])?; + + Some(CallBuilder::contract_swap_request( + *(supported_assets + .get(&src_token) + .ok_or(anyhow!("Source token {src_token:?} not found"))?), + try_into_primitive(amount)?, + try_into_primitive(dst_token)?, + try_into_encoded_address(try_into_primitive(dst_chain)?, dst_address.to_vec())?, + None, + event.tx_hash.into(), + vault_swap_parameters.broker_fees, + Some(vault_swap_parameters.refund_params), + vault_swap_parameters.dca_params, + vault_swap_parameters.boost_fee, + )) + }, VaultEvents::XcallNativeFilter(XcallNativeFilter { dst_chain, dst_address, @@ -99,7 +118,10 @@ where message, gas_amount, cf_parameters, - }) => + }) => { + let CfParameters { ccm_additional_data, vault_swap_parameters } = + CcmCfParameters::decode(&mut &cf_parameters[..])?; + Some(CallBuilder::contract_swap_request( native_asset, try_into_primitive(amount)?, @@ -118,13 +140,16 @@ where .try_into() .map_err(|_| anyhow!("Failed to deposit CCM: `message` too long."))?, gas_budget: try_into_primitive(gas_amount)?, - cf_parameters: cf_parameters.0.to_vec().try_into().map_err(|_| { - anyhow!("Failed to deposit CCM: `cf_parameters` too long.") - })?, + ccm_additional_data, }, }), event.tx_hash.into(), - )), + vault_swap_parameters.broker_fees, + Some(vault_swap_parameters.refund_params), + vault_swap_parameters.dca_params, + vault_swap_parameters.boost_fee, + )) + }, VaultEvents::XcallTokenFilter(XcallTokenFilter { dst_chain, dst_address, @@ -135,7 +160,10 @@ where message, gas_amount, cf_parameters, - }) => + }) => { + let CfParameters { ccm_additional_data, vault_swap_parameters } = + CcmCfParameters::decode(&mut &cf_parameters[..])?; + Some(CallBuilder::contract_swap_request( *(supported_assets .get(&src_token) @@ -156,13 +184,16 @@ where .try_into() .map_err(|_| anyhow!("Failed to deposit CCM. Message too long."))?, gas_budget: try_into_primitive(gas_amount)?, - cf_parameters: cf_parameters.0.to_vec().try_into().map_err(|_| { - anyhow!("Failed to deposit CCM. cf_parameters too long.") - })?, + ccm_additional_data, }, }), event.tx_hash.into(), - )), + vault_swap_parameters.broker_fees, + Some(vault_swap_parameters.refund_params), + vault_swap_parameters.dca_params, + vault_swap_parameters.boost_fee, + )) + }, VaultEvents::TransferNativeFailedFilter(TransferNativeFailedFilter { recipient, amount, @@ -204,6 +235,10 @@ pub trait IngressCallBuilder { destination_address: EncodedAddress, deposit_metadata: Option, tx_hash: cf_primitives::TransactionHash, + broker_fees: cf_primitives::Beneficiaries, + refund_params: Option, + dca_params: Option, + boost_fee: Option, ) -> state_chain_runtime::RuntimeCall; fn vault_transfer_failed( @@ -268,7 +303,7 @@ impl ChunkedByVaultBuilder { process_call(call, epoch.index).await; }, Err(message) => { - tracing::error!("Ignoring vault contract event: {message}"); + tracing::warn!("Ignoring vault contract event: {message}"); }, } } diff --git a/state-chain/cf-integration-tests/src/solana.rs b/state-chain/cf-integration-tests/src/solana.rs index e4aa87e81b..7a9e02e8de 100644 --- a/state-chain/cf-integration-tests/src/solana.rs +++ b/state-chain/cf-integration-tests/src/solana.rs @@ -392,7 +392,7 @@ fn solana_ccm_fails_with_invalid_input() { channel_metadata: CcmChannelMetadata { message: vec![0u8, 1u8, 2u8, 3u8].try_into().unwrap(), gas_budget: 0u128, - cf_parameters: vec![0u8, 1u8, 2u8, 3u8].try_into().unwrap(), + ccm_additional_data: vec![0u8, 1u8, 2u8, 3u8].try_into().unwrap(), }, }; @@ -466,7 +466,7 @@ fn solana_ccm_fails_with_invalid_input() { channel_metadata: CcmChannelMetadata { message: vec![0u8, 1u8, 2u8, 3u8].try_into().unwrap(), gas_budget: 0u128, - cf_parameters: SolCcmAccounts { + ccm_additional_data: SolCcmAccounts { cf_receiver: SolCcmAddress { pubkey: receiver.into(), is_writable: true }, remaining_accounts: vec![ SolCcmAddress { pubkey: SolPubkey([0x01; 32]), is_writable: false }, @@ -515,7 +515,7 @@ fn solana_ccm_fails_with_invalid_input() { egress_id: (ForeignChain::Solana, 1u64), error: ExecutexSwapAndCallError::FailedToBuildCcmForSolana( SolanaTransactionBuildingError::InvalidCcm( - CcmValidityError::CfParametersContainsInvalidAccounts + CcmValidityError::CcmAdditionalDataContainsInvalidAccounts ) ), }), @@ -691,7 +691,7 @@ fn solana_ccm_execution_error_can_trigger_fallback() { channel_metadata: CcmChannelMetadata { message: vec![0u8, 1u8, 2u8, 3u8].try_into().unwrap(), gas_budget: 1_000_000_000u128, - cf_parameters: SolCcmAccounts { + ccm_additional_data: SolCcmAccounts { cf_receiver: SolCcmAddress { pubkey: SolPubkey([0x10; 32]), is_writable: true }, remaining_accounts: vec![ SolCcmAddress { pubkey: SolPubkey([0x01; 32]), is_writable: false }, diff --git a/state-chain/cf-integration-tests/src/swapping.rs b/state-chain/cf-integration-tests/src/swapping.rs index 180b5b798d..e8f7f13e9c 100644 --- a/state-chain/cf-integration-tests/src/swapping.rs +++ b/state-chain/cf-integration-tests/src/swapping.rs @@ -374,7 +374,7 @@ fn can_process_ccm_via_swap_deposit_address() { let message = CcmChannelMetadata { message: vec![0u8, 1u8, 2u8, 3u8, 4u8].try_into().unwrap(), gas_budget: GAS_BUDGET, - cf_parameters: Default::default(), + ccm_additional_data: Default::default(), }; assert_ok!(Swapping::request_swap_deposit_address_with_affiliates( @@ -559,7 +559,7 @@ fn ccm_deposit_metadata_mock() -> CcmDepositMetadata { channel_metadata: CcmChannelMetadata { message: vec![0u8, 1u8, 2u8, 3u8, 4u8].try_into().unwrap(), gas_budget: 100_000_000, - cf_parameters: Default::default(), + ccm_additional_data: Default::default(), }, } } diff --git a/state-chain/chains/src/arb/api.rs b/state-chain/chains/src/arb/api.rs index 053827d9ee..e6a177a44d 100644 --- a/state-chain/chains/src/arb/api.rs +++ b/state-chain/chains/src/arb/api.rs @@ -70,7 +70,7 @@ where source_address: Option, gas_budget: ::ChainAmount, message: Vec, - _cf_parameters: Vec, + _ccm_additional_data: Vec, ) -> Result { let transfer_param = EncodableTransferAssetParams { asset: E::token_address(transfer_param.asset) diff --git a/state-chain/chains/src/btc/api.rs b/state-chain/chains/src/btc/api.rs index c60401de79..97a1f3d2d9 100644 --- a/state-chain/chains/src/btc/api.rs +++ b/state-chain/chains/src/btc/api.rs @@ -140,7 +140,7 @@ impl> ExecutexSwapAndCall for Bitc _source_address: Option, _gas_budget: ::ChainAmount, _message: Vec, - _cf_parameters: Vec, + _ccm_additional_data: Vec, ) -> Result { Err(ExecutexSwapAndCallError::Unsupported) } diff --git a/state-chain/chains/src/ccm_checker.rs b/state-chain/chains/src/ccm_checker.rs index b6a659c89e..9f0f617259 100644 --- a/state-chain/chains/src/ccm_checker.rs +++ b/state-chain/chains/src/ccm_checker.rs @@ -12,22 +12,22 @@ use sp_std::vec::Vec; #[derive(Copy, Clone, Debug, PartialEq, Eq, Encode, Decode, TypeInfo)] pub enum CcmValidityError { - CannotDecodeCfParameters, + CannotDecodeCcmAdditionalData, CcmIsTooLong, - CfParametersContainsInvalidAccounts, + CcmAdditionalDataContainsInvalidAccounts, } pub trait CcmValidityCheck { fn check_and_decode( _ccm: &CcmChannelMetadata, _egress_asset: cf_primitives::Asset, - ) -> Result { - Ok(DecodedCfParameters::NotRequired) + ) -> Result { + Ok(DecodedCcmAdditionalData::NotRequired) } } #[derive(Clone, Debug, PartialEq, Eq)] -pub enum DecodedCfParameters { +pub enum DecodedCcmAdditionalData { Solana(SolCcmAccounts), NotRequired, } @@ -41,11 +41,11 @@ impl CcmValidityCheck for CcmValidityChecker { fn check_and_decode( ccm: &CcmChannelMetadata, egress_asset: Asset, - ) -> Result { + ) -> Result { if ForeignChain::from(egress_asset) == ForeignChain::Solana { // Check if the cf_parameter can be decoded - let ccm_accounts = SolCcmAccounts::decode(&mut &ccm.cf_parameters.clone()[..]) - .map_err(|_| CcmValidityError::CannotDecodeCfParameters)?; + let ccm_accounts = SolCcmAccounts::decode(&mut &ccm.ccm_additional_data.clone()[..]) + .map_err(|_| CcmValidityError::CannotDecodeCcmAdditionalData)?; let asset: SolAsset = egress_asset .try_into() .expect("Only Solana chain's asset will be checked. This conversion must succeed."); @@ -61,9 +61,9 @@ impl CcmValidityCheck for CcmValidityChecker { return Err(CcmValidityError::CcmIsTooLong) } - Ok(DecodedCfParameters::Solana(ccm_accounts)) + Ok(DecodedCcmAdditionalData::Solana(ccm_accounts)) } else { - Ok(DecodedCfParameters::NotRequired) + Ok(DecodedCcmAdditionalData::NotRequired) } } } @@ -80,7 +80,7 @@ pub fn check_ccm_for_blacklisted_accounts( .iter() .any(|acc| acc.pubkey == blacklisted_account)) .then_some(()) - .ok_or(CcmValidityError::CfParametersContainsInvalidAccounts) + .ok_or(CcmValidityError::CcmAdditionalDataContainsInvalidAccounts) }) } @@ -98,7 +98,7 @@ mod test { let ccm = sol_test_values::ccm_parameter().channel_metadata; assert_eq!( CcmValidityChecker::check_and_decode(&ccm, Asset::Sol), - Ok(DecodedCfParameters::Solana(sol_test_values::ccm_accounts())) + Ok(DecodedCcmAdditionalData::Solana(sol_test_values::ccm_accounts())) ); } @@ -107,12 +107,12 @@ mod test { let ccm = CcmChannelMetadata { message: vec![0x01, 0x02, 0x03, 0x04, 0x05].try_into().unwrap(), gas_budget: 1, - cf_parameters: vec![0x01, 0x02, 0x03, 0x04, 0x05].try_into().unwrap(), + ccm_additional_data: vec![0x01, 0x02, 0x03, 0x04, 0x05].try_into().unwrap(), }; assert_err!( CcmValidityChecker::check_and_decode(&ccm, Asset::Sol), - CcmValidityError::CannotDecodeCfParameters + CcmValidityError::CannotDecodeCcmAdditionalData ); } @@ -121,7 +121,7 @@ mod test { let ccm = || CcmChannelMetadata { message: vec![0x01; MAX_CCM_BYTES_SOL].try_into().unwrap(), gas_budget: 0, - cf_parameters: SolCcmAccounts { + ccm_additional_data: SolCcmAccounts { cf_receiver: SolCcmAddress { pubkey: SolPubkey([0x01; 32]), is_writable: true }, remaining_accounts: vec![], fallback_address: SolPubkey([0xf0; 32]), @@ -141,7 +141,7 @@ mod test { ); let mut invalid_ccm = ccm(); - invalid_ccm.cf_parameters = SolCcmAccounts { + invalid_ccm.ccm_additional_data = SolCcmAccounts { cf_receiver: SolCcmAddress { pubkey: SolPubkey([0x01; 32]), is_writable: true }, remaining_accounts: vec![SolCcmAddress { pubkey: SolPubkey([0x01; 32]), @@ -163,7 +163,7 @@ mod test { let ccm = || CcmChannelMetadata { message: vec![0x01; MAX_CCM_BYTES_USDC].try_into().unwrap(), gas_budget: 0, - cf_parameters: SolCcmAccounts { + ccm_additional_data: SolCcmAccounts { cf_receiver: SolCcmAddress { pubkey: SolPubkey([0x01; 32]), is_writable: true }, fallback_address: SolPubkey([0xf0; 32]), remaining_accounts: vec![], @@ -183,7 +183,7 @@ mod test { ); let mut invalid_ccm = ccm(); - invalid_ccm.cf_parameters = SolCcmAccounts { + invalid_ccm.ccm_additional_data = SolCcmAccounts { cf_receiver: SolCcmAddress { pubkey: SolPubkey([0x01; 32]), is_writable: true }, remaining_accounts: vec![SolCcmAddress { pubkey: SolPubkey([0x01; 32]), @@ -219,31 +219,31 @@ mod test { // Always valid on other chains. assert_ok!( CcmValidityChecker::check_and_decode(&ccm, Asset::Eth), - DecodedCfParameters::NotRequired + DecodedCcmAdditionalData::NotRequired ); assert_ok!( CcmValidityChecker::check_and_decode(&ccm, Asset::Btc), - DecodedCfParameters::NotRequired + DecodedCcmAdditionalData::NotRequired ); assert_ok!( CcmValidityChecker::check_and_decode(&ccm, Asset::Flip), - DecodedCfParameters::NotRequired + DecodedCcmAdditionalData::NotRequired ); assert_ok!( CcmValidityChecker::check_and_decode(&ccm, Asset::Usdt), - DecodedCfParameters::NotRequired + DecodedCcmAdditionalData::NotRequired ); assert_ok!( CcmValidityChecker::check_and_decode(&ccm, Asset::Usdc), - DecodedCfParameters::NotRequired + DecodedCcmAdditionalData::NotRequired ); assert_ok!( CcmValidityChecker::check_and_decode(&ccm, Asset::ArbUsdc), - DecodedCfParameters::NotRequired + DecodedCcmAdditionalData::NotRequired ); assert_ok!( CcmValidityChecker::check_and_decode(&ccm, Asset::ArbEth), - DecodedCfParameters::NotRequired + DecodedCcmAdditionalData::NotRequired ); } @@ -267,7 +267,7 @@ mod test { }; assert_err!( check_ccm_for_blacklisted_accounts(&ccm_accounts, blacklisted_accounts()), - CcmValidityError::CfParametersContainsInvalidAccounts + CcmValidityError::CcmAdditionalDataContainsInvalidAccounts ); let ccm_accounts = SolCcmAccounts { @@ -286,7 +286,7 @@ mod test { }; assert_err!( check_ccm_for_blacklisted_accounts(&ccm_accounts, blacklisted_accounts()), - CcmValidityError::CfParametersContainsInvalidAccounts + CcmValidityError::CcmAdditionalDataContainsInvalidAccounts ); // Agg key is blacklisted @@ -303,7 +303,7 @@ mod test { }; assert_err!( check_ccm_for_blacklisted_accounts(&ccm_accounts, blacklisted_accounts()), - CcmValidityError::CfParametersContainsInvalidAccounts + CcmValidityError::CcmAdditionalDataContainsInvalidAccounts ); let ccm_accounts = SolCcmAccounts { @@ -319,7 +319,7 @@ mod test { }; assert_err!( check_ccm_for_blacklisted_accounts(&ccm_accounts, blacklisted_accounts()), - CcmValidityError::CfParametersContainsInvalidAccounts + CcmValidityError::CcmAdditionalDataContainsInvalidAccounts ); } } diff --git a/state-chain/chains/src/dot/api.rs b/state-chain/chains/src/dot/api.rs index d42186f812..5cd8443f2e 100644 --- a/state-chain/chains/src/dot/api.rs +++ b/state-chain/chains/src/dot/api.rs @@ -128,7 +128,7 @@ where _source_address: Option, _gas_budget: ::ChainAmount, _message: Vec, - _cf_parameters: Vec, + _ccm_additional_data: Vec, ) -> Result { Err(ExecutexSwapAndCallError::Unsupported) } diff --git a/state-chain/chains/src/eth/api.rs b/state-chain/chains/src/eth/api.rs index 1244ac31b2..9eb4121f57 100644 --- a/state-chain/chains/src/eth/api.rs +++ b/state-chain/chains/src/eth/api.rs @@ -194,7 +194,7 @@ where source_address: Option, gas_budget: ::ChainAmount, message: Vec, - _cf_parameters: Vec, + _ccm_additional_data: Vec, ) -> Result { let transfer_param = EncodableTransferAssetParams { asset: E::token_address(transfer_param.asset) diff --git a/state-chain/chains/src/lib.rs b/state-chain/chains/src/lib.rs index 8eb73ad7fd..2b284c2b56 100644 --- a/state-chain/chains/src/lib.rs +++ b/state-chain/chains/src/lib.rs @@ -550,7 +550,7 @@ pub trait ExecutexSwapAndCall: ApiCall { source_address: Option, gas_budget: C::ChainAmount, message: Vec, - cf_parameters: Vec, + ccm_additional_data: Vec, ) -> Result; } @@ -589,10 +589,10 @@ pub enum SwapOrigin { } pub const MAX_CCM_MSG_LENGTH: u32 = 10_000; -pub const MAX_CCM_CF_PARAM_LENGTH: u32 = 1_000; +pub const MAX_CCM_ADDITIONAL_DATA_LENGTH: u32 = 1_000; pub type CcmMessage = BoundedVec>; -pub type CcmCfParameters = BoundedVec>; +pub type CcmAdditionalData = BoundedVec>; #[cfg(feature = "std")] mod bounded_hex { @@ -637,7 +637,7 @@ pub struct CcmChannelMetadata { feature = "std", serde(with = "bounded_hex", default, skip_serializing_if = "Vec::is_empty") )] - pub cf_parameters: CcmCfParameters, + pub ccm_additional_data: CcmAdditionalData, } #[derive(Debug, Clone, PartialEq, Eq, Encode, Decode, TypeInfo)] diff --git a/state-chain/chains/src/sol/api.rs b/state-chain/chains/src/sol/api.rs index 1b330d9ff9..b86ead8290 100644 --- a/state-chain/chains/src/sol/api.rs +++ b/state-chain/chains/src/sol/api.rs @@ -10,7 +10,7 @@ use sp_std::{vec, vec::Vec}; use crate::{ ccm_checker::{ check_ccm_for_blacklisted_accounts, CcmValidityCheck, CcmValidityChecker, CcmValidityError, - DecodedCfParameters, + DecodedCcmAdditionalData, }, sol::{ transaction_builder::SolanaTransactionBuilder, SolAddress, SolAmount, SolApiEnvironment, @@ -264,10 +264,10 @@ impl SolanaApi { source_address: Option, gas_budget: ::ChainAmount, message: Vec, - cf_parameters: Vec, + ccm_additional_data: Vec, ) -> Result { // For extra safety, re-verify the validity of the CCM message here - // and extract the decoded `ccm_accounts` from `cf_parameters`. + // and extract the decoded `ccm_accounts` from `ccm_additional_data`. let decoded_cf_params = CcmValidityChecker::check_and_decode( &CcmChannelMetadata { message: message @@ -275,7 +275,7 @@ impl SolanaApi { .try_into() .expect("This is parsed from bounded vec, therefore the size must fit"), gas_budget: 0, // This value is un-used by Solana - cf_parameters: cf_parameters + ccm_additional_data: ccm_additional_data .clone() .try_into() .expect("This is parsed from bounded vec, therefore the size must fit"), @@ -284,12 +284,14 @@ impl SolanaApi { ) .map_err(SolanaTransactionBuildingError::InvalidCcm)?; - // Always expects the `DecodedCfParameters::Solana(..)` variant of the decoded cf params. - let ccm_accounts = if let DecodedCfParameters::Solana(ccm_accounts) = decoded_cf_params { + // Always expects the `DecodedCcmAdditionalData::Solana(..)` variant of the decoded cf + // params. + let ccm_accounts = if let DecodedCcmAdditionalData::Solana(ccm_accounts) = decoded_cf_params + { Ok(ccm_accounts) } else { Err(SolanaTransactionBuildingError::InvalidCcm( - CcmValidityError::CannotDecodeCfParameters, + CcmValidityError::CannotDecodeCcmAdditionalData, )) }?; @@ -449,7 +451,7 @@ impl ExecutexSwapAndCall for SolanaApi source_address: Option, gas_budget: ::ChainAmount, message: Vec, - cf_parameters: Vec, + ccm_additional_data: Vec, ) -> Result { Self::ccm_transfer( transfer_param, @@ -457,7 +459,7 @@ impl ExecutexSwapAndCall for SolanaApi source_address, gas_budget, message, - cf_parameters, + ccm_additional_data, ) .map_err(|e| { log::error!("Failed to construct Solana CCM transfer transaction! \nError: {:?}", e); diff --git a/state-chain/chains/src/sol/sol_tx_core.rs b/state-chain/chains/src/sol/sol_tx_core.rs index 7a29fad8e3..14bddb13a8 100644 --- a/state-chain/chains/src/sol/sol_tx_core.rs +++ b/state-chain/chains/src/sol/sol_tx_core.rs @@ -950,7 +950,7 @@ pub mod sol_test_values { channel_metadata: CcmChannelMetadata { message: vec![124u8, 29u8, 15u8, 7u8].try_into().unwrap(), // CCM message gas_budget: 0u128, // unused - cf_parameters: codec::Encode::encode(&ccm_accounts()) + ccm_additional_data: codec::Encode::encode(&ccm_accounts()) .try_into() .expect("Test data cannot be too long"), // Extra addresses }, diff --git a/state-chain/pallets/cf-ingress-egress/src/benchmarking.rs b/state-chain/pallets/cf-ingress-egress/src/benchmarking.rs index 22b95c7ec6..e5dc6a8942 100644 --- a/state-chain/pallets/cf-ingress-egress/src/benchmarking.rs +++ b/state-chain/pallets/cf-ingress-egress/src/benchmarking.rs @@ -328,7 +328,7 @@ mod benchmarks { channel_metadata: CcmChannelMetadata { message: vec![0x00].try_into().unwrap(), gas_budget: 1, - cf_parameters: Default::default(), + ccm_additional_data: Default::default(), }, }; let call = Call::::contract_swap_request { diff --git a/state-chain/pallets/cf-ingress-egress/src/lib.rs b/state-chain/pallets/cf-ingress-egress/src/lib.rs index 4e1c786628..7b8388bb35 100644 --- a/state-chain/pallets/cf-ingress-egress/src/lib.rs +++ b/state-chain/pallets/cf-ingress-egress/src/lib.rs @@ -27,7 +27,7 @@ use cf_chains::{ }, assets::any::GetChainAssetMap, ccm_checker::CcmValidityCheck, - AllBatch, AllBatchError, CcmCfParameters, CcmChannelMetadata, CcmDepositMetadata, + AllBatch, AllBatchError, CcmAdditionalData, CcmChannelMetadata, CcmDepositMetadata, CcmFailReason, CcmMessage, Chain, ChannelLifecycleHooks, ChannelRefundParameters, ConsolidateCall, DepositChannel, ExecutexSwapAndCall, FetchAssetParams, ForeignChainAddress, SwapOrigin, TransferAssetParams, @@ -160,7 +160,7 @@ pub(crate) struct CrossChainMessage { pub source_chain: ForeignChain, pub source_address: Option, // Where funds might be returned to if the message fails. - pub cf_parameters: CcmCfParameters, + pub ccm_additional_data: CcmAdditionalData, pub gas_budget: C::ChainAmount, } @@ -1538,7 +1538,7 @@ impl, I: 'static> Pallet { ccm.source_address, ccm.gas_budget, ccm.message.to_vec(), - ccm.cf_parameters.to_vec(), + ccm.ccm_additional_data.to_vec(), ) { Ok(api_call) => { let broadcast_id = T::Broadcaster::threshold_sign_and_broadcast_with_callback( @@ -2383,7 +2383,7 @@ impl, I: 'static> EgressApi for Pallet { match maybe_ccm_with_gas_budget { Some(( CcmDepositMetadata { - channel_metadata: CcmChannelMetadata { message, cf_parameters, .. }, + channel_metadata: CcmChannelMetadata { message, ccm_additional_data, .. }, source_chain, source_address, .. @@ -2396,7 +2396,7 @@ impl, I: 'static> EgressApi for Pallet { amount, destination_address: destination_address.clone(), message, - cf_parameters, + ccm_additional_data, source_chain, source_address, gas_budget, diff --git a/state-chain/pallets/cf-ingress-egress/src/tests.rs b/state-chain/pallets/cf-ingress-egress/src/tests.rs index 30dde61e93..8269f302b3 100644 --- a/state-chain/pallets/cf-ingress-egress/src/tests.rs +++ b/state-chain/pallets/cf-ingress-egress/src/tests.rs @@ -117,7 +117,7 @@ fn blacklisted_asset_will_not_egress_via_ccm() { channel_metadata: CcmChannelMetadata { message: vec![0x00, 0x01, 0x02].try_into().unwrap(), gas_budget: 1_000, - cf_parameters: vec![].try_into().unwrap(), + ccm_additional_data: vec![].try_into().unwrap(), }, }; @@ -151,7 +151,7 @@ fn blacklisted_asset_will_not_egress_via_ccm() { message: ccm.channel_metadata.message.clone(), source_chain: ForeignChain::Ethereum, source_address: ccm.source_address.clone(), - cf_parameters: ccm.channel_metadata.cf_parameters, + ccm_additional_data: ccm.channel_metadata.ccm_additional_data, gas_budget, }] ); @@ -487,7 +487,7 @@ fn can_egress_ccm() { channel_metadata: CcmChannelMetadata { message: vec![0x00, 0x01, 0x02].try_into().unwrap(), gas_budget: GAS_BUDGET, - cf_parameters: vec![].try_into().unwrap(), + ccm_additional_data: vec![].try_into().unwrap(), } }; @@ -507,7 +507,7 @@ fn can_egress_ccm() { amount, destination_address, message: ccm.channel_metadata.message.clone(), - cf_parameters: vec![].try_into().unwrap(), + ccm_additional_data: vec![].try_into().unwrap(), source_chain: ForeignChain::Ethereum, source_address: Some(ForeignChainAddress::Eth([0xcf; 20].into())), gas_budget: GAS_BUDGET, @@ -1736,7 +1736,7 @@ fn do_not_process_more_ccm_swaps_than_allowed_by_limit() { channel_metadata: CcmChannelMetadata { message: vec![0x00, 0x01, 0x02].try_into().unwrap(), gas_budget: 1_000, - cf_parameters: vec![].try_into().unwrap(), + ccm_additional_data: vec![].try_into().unwrap(), }, }; @@ -1824,7 +1824,7 @@ fn can_request_ccm_swap_via_extrinsic() { channel_metadata: CcmChannelMetadata { message: vec![0x01].try_into().unwrap(), gas_budget: 1_000, - cf_parameters: Default::default(), + ccm_additional_data: Default::default(), }, }; @@ -1925,7 +1925,7 @@ fn failed_ccm_deposit_can_deposit_event() { channel_metadata: CcmChannelMetadata { message: vec![0x01].try_into().unwrap(), gas_budget: GAS_BUDGET, - cf_parameters: Default::default(), + ccm_additional_data: Default::default(), }, }; diff --git a/state-chain/pallets/cf-swapping/src/tests.rs b/state-chain/pallets/cf-swapping/src/tests.rs index deaa23c184..ac79413503 100644 --- a/state-chain/pallets/cf-swapping/src/tests.rs +++ b/state-chain/pallets/cf-swapping/src/tests.rs @@ -216,7 +216,7 @@ fn generate_ccm_channel() -> CcmChannelMetadata { CcmChannelMetadata { message: vec![0x01].try_into().unwrap(), gas_budget: GAS_BUDGET, - cf_parameters: Default::default(), + ccm_additional_data: Default::default(), } } fn generate_ccm_deposit() -> CcmDepositMetadata { diff --git a/state-chain/pallets/cf-swapping/src/tests/ccm.rs b/state-chain/pallets/cf-swapping/src/tests/ccm.rs index 7b2c9712d8..f06002224a 100644 --- a/state-chain/pallets/cf-swapping/src/tests/ccm.rs +++ b/state-chain/pallets/cf-swapping/src/tests/ccm.rs @@ -66,7 +66,7 @@ pub(super) fn assert_ccm_egressed( amount: principal_amount, destination_address: (*EVM_OUTPUT_ADDRESS).clone(), message: vec![0x01].try_into().unwrap(), - cf_parameters: vec![].try_into().unwrap(), + ccm_additional_data: vec![].try_into().unwrap(), gas_budget, }, ); diff --git a/state-chain/primitives/src/lib.rs b/state-chain/primitives/src/lib.rs index 6daa6b210e..b5c584df1a 100644 --- a/state-chain/primitives/src/lib.rs +++ b/state-chain/primitives/src/lib.rs @@ -399,13 +399,26 @@ pub type Affiliates = BoundedVec, ConstU32>; pub type Beneficiaries = BoundedVec, ConstU32<{ MAX_AFFILIATES + 1 }>>; -#[derive(Clone, Debug, PartialEq, Eq, Encode, Decode, TypeInfo, Serialize, Deserialize)] +#[derive( + Clone, Debug, PartialEq, Eq, MaxEncodedLen, Encode, Decode, TypeInfo, Serialize, Deserialize, +)] pub struct Beneficiary { pub account: Id, pub bps: BasisPoints, } -#[derive(Clone, RuntimeDebug, PartialEq, Eq, Encode, Decode, TypeInfo, Serialize, Deserialize)] +#[derive( + Clone, + RuntimeDebug, + PartialEq, + Eq, + Encode, + Decode, + MaxEncodedLen, + TypeInfo, + Serialize, + Deserialize, +)] pub struct DcaParameters { /// The number of individual swaps to be executed pub number_of_chunks: u32, diff --git a/state-chain/traits/src/mocks/api_call.rs b/state-chain/traits/src/mocks/api_call.rs index 095b58c28f..05ee49ecdc 100644 --- a/state-chain/traits/src/mocks/api_call.rs +++ b/state-chain/traits/src/mocks/api_call.rs @@ -149,7 +149,7 @@ impl ExecutexSwapAndCall for MockEthereumApiCall { source_address: Option, gas_budget: ::ChainAmount, message: Vec, - _cf_parameters: Vec, + _ccm_additional_data: Vec, ) -> Result { if MockEvmEnvironment::lookup(transfer_param.asset).is_none() { Err(ExecutexSwapAndCallError::DispatchError(DispatchError::CannotLookup)) @@ -285,7 +285,7 @@ impl ExecutexSwapAndCall for MockBitcoinApiCall { source_address: Option, gas_budget: ::ChainAmount, message: Vec, - _cf_parameters: Vec, + _ccm_additional_data: Vec, ) -> Result { if MockBtcEnvironment::lookup(transfer_param.asset).is_none() { Err(ExecutexSwapAndCallError::DispatchError(DispatchError::CannotLookup)) diff --git a/state-chain/traits/src/mocks/egress_handler.rs b/state-chain/traits/src/mocks/egress_handler.rs index b150ef96e4..d6fdc50160 100644 --- a/state-chain/traits/src/mocks/egress_handler.rs +++ b/state-chain/traits/src/mocks/egress_handler.rs @@ -1,6 +1,6 @@ use super::{MockPallet, MockPalletStorage}; use crate::{EgressApi, ScheduledEgressDetails}; -use cf_chains::{CcmCfParameters, CcmDepositMetadata, CcmMessage, Chain}; +use cf_chains::{CcmAdditionalData, CcmDepositMetadata, CcmMessage, Chain}; use cf_primitives::{AssetAmount, EgressCounter}; use codec::{Decode, Encode}; use frame_support::sp_runtime::{ @@ -29,7 +29,7 @@ pub enum MockEgressParameter { amount: C::ChainAmount, destination_address: C::ChainAccount, message: CcmMessage, - cf_parameters: CcmCfParameters, + ccm_additional_data: CcmAdditionalData, gas_budget: C::ChainAmount, }, } @@ -95,7 +95,7 @@ impl EgressApi for MockEgressHandler { amount, destination_address, message: message.channel_metadata.message.clone(), - cf_parameters: message.channel_metadata.cf_parameters.clone(), + ccm_additional_data: message.channel_metadata.ccm_additional_data.clone(), gas_budget: *gas_budget, }, None => MockEgressParameter::::Swap {