Skip to content

Commit

Permalink
Merge branch 'main' into test/fix-submission-watcher-test2
Browse files Browse the repository at this point in the history
  • Loading branch information
j4m1ef0rd committed Oct 30, 2024
2 parents 2986d80 + 079a149 commit c7a4de2
Show file tree
Hide file tree
Showing 71 changed files with 1,468 additions and 1,130 deletions.
351 changes: 183 additions & 168 deletions Cargo.lock

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -147,8 +147,8 @@ impl From<DepositInfo<Bitcoin>> for WitnessInformation {
amount: value.amount.into(),
asset: value.asset.into(),
deposit_details: Some(DepositDetails::Bitcoin {
tx_id: value.deposit_details.utxo_id.tx_id,
vout: value.deposit_details.utxo_id.vout,
tx_id: value.deposit_details.id.tx_id,
vout: value.deposit_details.id.vout,
}),
}
}
Expand Down
2 changes: 1 addition & 1 deletion bouncer/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
18 changes: 9 additions & 9 deletions bouncer/pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

110 changes: 62 additions & 48 deletions bouncer/shared/contract_swap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,44 +5,67 @@ 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,
observeCcmReceived,
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<typeof executeSwap> {
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.
Expand All @@ -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,
Expand All @@ -86,37 +115,18 @@ export async function performSwapViaContract(
messageMetadata?: CcmDepositMetadata,
swapContext?: SwapContext,
log = true,
amount?: string,
boostFeeBps?: number,
fillOrKillParams?: FillOrKillParamsX128,
dcaParams?: DcaParams,
): Promise<ContractSwapParams> {
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) {
Expand All @@ -133,8 +143,12 @@ export async function performSwapViaContract(
sourceAsset,
destAsset,
destAddress,
wallet,
messageMetadata,
amountToSwap,
boostFeeBps,
fillOrKillParams,
dcaParams,
wallet,
);
swapContext?.updateStatus(swapTag, SwapStatus.ContractExecuted);

Expand Down Expand Up @@ -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
{
Expand Down
2 changes: 1 addition & 1 deletion bouncer/shared/new_swap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
2 changes: 1 addition & 1 deletion bouncer/shared/perform_swap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
4 changes: 1 addition & 3 deletions bouncer/shared/send.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
11 changes: 1 addition & 10 deletions bouncer/shared/swap_context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@ import assert from 'assert';
export enum SwapStatus {
Initiated,
Funded,
// Contract swap specific statuses
ContractApproved,
ContractExecuted,
SwapScheduled,
Success,
Expand Down Expand Up @@ -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;
Expand Down
16 changes: 8 additions & 8 deletions bouncer/shared/swapping.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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();
Expand All @@ -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.
Expand All @@ -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':
Expand All @@ -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}`);
Expand Down Expand Up @@ -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(
Expand All @@ -162,7 +162,7 @@ export function newCcmMetadata(
return {
message,
gasBudget,
cfParameters,
ccmAdditionalData,
};
}

Expand Down Expand Up @@ -250,7 +250,7 @@ export async function testSwapViaContract(
destAsset,
addressType,
messageMetadata,
(tagSuffix ?? '') + ' Contract',
(tagSuffix ?? '') + 'Contract',
log,
swapContext,
);
Expand Down
Loading

0 comments on commit c7a4de2

Please sign in to comment.