From c298534ba7a27a063009910ee0e0e2c581fdce58 Mon Sep 17 00:00:00 2001 From: Pierre-Alain Date: Wed, 28 Feb 2024 11:09:51 +0900 Subject: [PATCH] feat: Aurora Cloud silo support. (#93) * feat: Aurora Cloud silo support. * chore: Yarn version check. * feat: Silo with different native currency support. * chore: Yarn version check. * feat: aurora-nep141 bridged-ether silo support. * feat: Record auroraChainId is transfer. * feat: Unwrap wNEAR option. * fix: Replace last getAuroraProvider with getAuroraCloudProvider. * fix: More robust bridge proof receipt parsing. --- .pnp.cjs | 10 + ...-bn.js-npm-5.1.5-c2195eccd3-9719330c86.zip | Bin 0 -> 4512 bytes .yarn/versions/5012e96b.yml | 10 + .../src/bridged-erc20/sendToEthereum/index.ts | 125 +++++------- .../src/natural-erc20/sendToAurora/index.ts | 7 +- .../src/bridged-ether/sendToEthereum/index.ts | 135 +++++++------ .../src/natural-ether/sendToAurora/index.ts | 17 +- .../src/bridged-erc20/getBalance.ts | 5 +- .../src/bridged-erc20/sendToNear/index.ts | 28 ++- .../src/bridged-ether/sendToNear/index.ts | 42 ++-- .../src/natural-nep141/sendToAurora/index.ts | 7 +- packages/client/src/index.ts | 2 + packages/client/src/utils.ts | 40 ++++ .../src/bridged-ether/sendToEthereum/index.ts | 74 ++----- .../src/natural-near/sendToEthereum/index.ts | 66 ++----- .../bridged-nep141/sendToEthereum/index.ts | 68 ++----- packages/utils/package.json | 2 + packages/utils/src/findProof.ts | 184 +++++++++++++++++- packages/utils/src/index.ts | 8 +- yarn.lock | 11 ++ 20 files changed, 501 insertions(+), 340 deletions(-) create mode 100644 .yarn/cache/@types-bn.js-npm-5.1.5-c2195eccd3-9719330c86.zip create mode 100644 .yarn/versions/5012e96b.yml diff --git a/.pnp.cjs b/.pnp.cjs index 74b1d773..f664d973 100755 --- a/.pnp.cjs +++ b/.pnp.cjs @@ -999,8 +999,10 @@ const RAW_RUNTIME_STATE = "packageLocation": "./packages/utils/",\ "packageDependencies": [\ ["@near-eth/utils", "workspace:packages/utils"],\ + ["@types/bn.js", "npm:5.1.5"],\ ["@types/bs58", "npm:4.0.1"],\ ["@types/node", "npm:14.18.21"],\ + ["bn.js", "npm:5.2.1"],\ ["bs58", "npm:4.0.1"],\ ["eth-object", "https://github.com/aurora-is-near/eth-object.git#commit=378b8dbf44a71f7049666cea5a16ab88d45aed06"],\ ["ethereumjs-util", "npm:7.1.5"],\ @@ -1124,6 +1126,14 @@ const RAW_RUNTIME_STATE = ["@types/node", "npm:18.0.0"]\ ],\ "linkType": "HARD"\ + }],\ + ["npm:5.1.5", {\ + "packageLocation": "./.yarn/cache/@types-bn.js-npm-5.1.5-c2195eccd3-9719330c86.zip/node_modules/@types/bn.js/",\ + "packageDependencies": [\ + ["@types/bn.js", "npm:5.1.5"],\ + ["@types/node", "npm:18.0.0"]\ + ],\ + "linkType": "HARD"\ }]\ ]],\ ["@types/bs58", [\ diff --git a/.yarn/cache/@types-bn.js-npm-5.1.5-c2195eccd3-9719330c86.zip b/.yarn/cache/@types-bn.js-npm-5.1.5-c2195eccd3-9719330c86.zip new file mode 100644 index 0000000000000000000000000000000000000000..25347ade6d7b627a04dc5cc9a5c8722ae0619ad3 GIT binary patch literal 4512 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 { } } -/** - * Parse the burn receipt id and block height needed to complete - * the step BURN - * @param nearBurnTx - * @param nep141Factory - * @param nearProvider - */ -export async function parseBurnReceipt ( - nearBurnTx: FinalExecutionOutcome, - nep141Factory: string, - nearProvider: najProviders.Provider -): Promise<{id: string, blockHeight: number, blockTimestamp: number, event: { amount: string, token: string, recipient: string }}> { - // @ts-expect-error - const bridgeReceipt: any = nearBurnTx.receipts_outcome.find(r => r.outcome.executor_id === nep141Factory) - if (!bridgeReceipt) { - throw new Error(`Failed to parse bridge receipt for ${JSON.stringify(nearBurnTx)}`) - } - const successValue = bridgeReceipt.outcome.status.SuccessValue - // eslint-disable-next-line @typescript-eslint/no-extraneous-class - class BurnEvent { - constructor (args: any) { - Object.assign(this, args) - } - } - const SCHEMA = new Map([ - [BurnEvent, { - kind: 'struct', - fields: [ - ['flag', 'u8'], - ['amount', 'u128'], - ['token', [20]], - ['recipient', [20]] - ] - }] - ]) - const rawEvent = deserializeBorsh( - SCHEMA, BurnEvent, Buffer.from(successValue, 'base64') - ) as { amount: BN, token: Uint8Array, recipient: Uint8Array } - const event = { - amount: rawEvent.amount.toString(), - token: '0x' + Buffer.from(rawEvent.token).toString('hex'), - recipient: '0x' + Buffer.from(rawEvent.recipient).toString('hex') - } - - const receiptBlock = await nearProvider.block({ blockId: bridgeReceipt.block_hash }) - const blockHeight = Number(receiptBlock.header.height) - const blockTimestamp = Number(receiptBlock.header.timestamp) - return { id: bridgeReceipt.id, blockHeight, blockTimestamp, event } -} - export async function findAllTransactions ( { fromBlock, toBlock, sender, erc20Address, options }: { fromBlock: number | string @@ -258,7 +209,7 @@ export async function findAllTransactions ( ): Promise { options = options ?? {} const bridgeParams = getBridgeParams() - const provider = options.provider ?? getAuroraProvider() + const provider = options.provider ?? getAuroraCloudProvider({ auroraEvmAccount: options?.auroraEvmAccount }) const auroraErc20Address = options.auroraErc20Address ?? await getAuroraErc20Address( { erc20Address, options } ) @@ -318,7 +269,7 @@ export async function recover ( options.nearAccount?.connection.provider ?? getNearProvider() - const auroraProvider = options.auroraProvider ?? getAuroraProvider() + const auroraProvider = options.auroraProvider ?? getAuroraCloudProvider({ auroraEvmAccount: options?.auroraEvmAccount }) // Ethers formats the receipts and removes nearTransactionHash const auroraBurnReceipt = await auroraProvider.send('eth_getTransactionReceipt', [burnTxHash]) const decodedTxHash = Buffer.from(auroraBurnReceipt.nearTransactionHash.slice(2), 'hex') @@ -345,12 +296,28 @@ export async function recover ( throw new Error(`Withdraw transaction failed: ${burnTxHash}`) } - const nep141Factory = options.nep141Factory ?? getBridgeParams().nep141Factory - const nearBurnReceipt = await parseBurnReceipt(burnTx, nep141Factory, nearProvider) - - const { amount, recipient, token: erc20Address } = nearBurnReceipt.event - const symbol = options.symbol ?? await getSymbol({ erc20Address, options }) - const decimals = options.decimals ?? await getDecimals({ erc20Address, options }) + const bridgeParams = getBridgeParams() + const nep141Factory = options.nep141Factory ?? bridgeParams.nep141Factory + let nearBurnReceipt, amount, recipient, symbol, decimals + if (options.symbol !== 'ETH') { + nearBurnReceipt = await parseNep141BurnReceipt(burnTx, nep141Factory, nearProvider) + amount = nearBurnReceipt.event.amount + recipient = nearBurnReceipt.event.recipient + const { token: erc20Address } = nearBurnReceipt.event + symbol = options.symbol ?? await getSymbol({ erc20Address, options }) + decimals = options.decimals ?? await getDecimals({ erc20Address, options }) + } else { + // Withdraw ERC-20 ETH from a silo which doesn't use ETH as native currency. + const auroraEvmAccount = options.auroraEvmAccount ?? bridgeParams.auroraEvmAccount + nearBurnReceipt = await parseETHBurnReceipt(burnTx, auroraEvmAccount, nearProvider) + amount = nearBurnReceipt.event.amount + recipient = nearBurnReceipt.event.recipient + symbol = options.symbol + decimals = options.decimals + if (!symbol || !decimals || !auroraEvmAccount) { + throw new Error('Must provide symbol and decimals options to reciver ETH transfer from silo with different native currency') + } + } const sourceTokenName = 'a' + symbol const destinationTokenName = symbol @@ -372,6 +339,8 @@ export async function recover ( sourceToken, symbol, decimals, + auroraEvmAccount: options.auroraEvmAccount ?? bridgeParams.auroraEvmAccount, + auroraChainId: options.auroraChainId ?? bridgeParams.auroraChainId, burnHashes: [burnTxHash], nearBurnHashes: [nearBurnTxHash], nearBurnReceiptIds: [nearBurnReceipt.id], @@ -395,9 +364,9 @@ export async function recover ( * @param params.options.auroraChainId Aurora chain id of the bridge. * @param params.options.auroraErc20Abi Aurora ERC-20 abi to call withdrawToEthereum. * @param params.options.auroraErc20Address params.erc20Address's address on Aurora. - * @param options.auroraEvmAccount Aurora account on NEAR. + * @param params.options.auroraEvmAccount Aurora Cloud silo account on NEAR. * @param params.options.nep141Factory ERC-20 connector factory to determine the NEAR address. - * @param options.provider Aurora provider to use. + * @param params.options.provider Aurora provider to use. * @param params.options.nearAccount Connected NEAR wallet account to use. * @param params.options.nearProvider NEAR provider. * @param params.options.signer Ethers signer to use. @@ -440,9 +409,10 @@ export async function initiate ( const signer = options.signer ?? provider.getSigner() const sender = options.sender ?? (await signer.getAddress()).toLowerCase() + const bridgeParams = getBridgeParams() // various attributes stored as arrays, to keep history of retries - let transfer = { + let transfer: Transfer = { ...transferDraft, id: Math.random().toString().slice(2), @@ -453,6 +423,8 @@ export async function initiate ( sender, sourceToken: auroraErc20Address, sourceTokenName, + auroraEvmAccount: options.auroraEvmAccount ?? bridgeParams.auroraEvmAccount, + auroraChainId: options.auroraChainId ?? bridgeParams.auroraChainId, symbol, decimals } @@ -534,12 +506,12 @@ export async function checkBurn ( ): Promise { options = options ?? {} const bridgeParams = getBridgeParams() - const provider = options.provider ?? getAuroraProvider() + const provider = options.provider ?? getAuroraCloudProvider({ auroraEvmAccount: transfer.auroraEvmAccount }) const burnHash = last(transfer.burnHashes) const ethChainId: number = (await provider.getNetwork()).chainId - const expectedChainId: number = options.auroraChainId ?? bridgeParams.auroraChainId + const expectedChainId: number = options.auroraChainId ?? transfer.auroraChainId ?? bridgeParams.auroraChainId if (ethChainId !== expectedChainId) { throw new Error( `Wrong aurora network for checkLock, expected: ${expectedChainId}, got: ${ethChainId}` @@ -628,7 +600,12 @@ export async function checkBurn ( let nearBurnReceipt const nep141Factory = options.nep141Factory ?? getBridgeParams().nep141Factory try { - nearBurnReceipt = await parseBurnReceipt(nearBurnTx, nep141Factory, nearProvider) + if (transfer.symbol !== 'ETH') { + nearBurnReceipt = await parseNep141BurnReceipt(nearBurnTx, nep141Factory, nearProvider) + } else { + // Withdraw ERC-20 ETH from a silo which doesn't use ETH as native currency. + nearBurnReceipt = await parseETHBurnReceipt(nearBurnTx, transfer.auroraEvmAccount ?? 'aurora', nearProvider) + } } catch (e) { if (e instanceof TransferError) { return { @@ -741,7 +718,11 @@ export async function checkSync ( if (nearOnEthClientBlockHeight > burnBlockHeight) { proof = await findNearProof( last(transfer.nearBurnReceiptIds), - options.nep141Factory ?? bridgeParams.nep141Factory, + // NOTE: If ETH is being transfered with @near-eth/aurora-erc20, + // it means that ETH is not the silo's native currency + transfer.symbol === 'ETH' + ? bridgeParams.auroraEvmAccount + : (options.nep141Factory ?? bridgeParams.nep141Factory), nearOnEthClientBlockHeight, nearProvider, provider, diff --git a/packages/aurora-erc20/src/natural-erc20/sendToAurora/index.ts b/packages/aurora-erc20/src/natural-erc20/sendToAurora/index.ts index 31aaa99a..e55b6059 100644 --- a/packages/aurora-erc20/src/natural-erc20/sendToAurora/index.ts +++ b/packages/aurora-erc20/src/natural-erc20/sendToAurora/index.ts @@ -53,6 +53,7 @@ export interface Transfer extends TransferDraft, TransactionInfo { checkSyncInterval?: number nextCheckSyncTimestamp?: Date proof?: Uint8Array + auroraEvmAccount?: string } export interface TransferOptions { @@ -272,6 +273,7 @@ export async function recover ( sourceTokenName, symbol, decimals, + auroraEvmAccount: options.auroraEvmAccount ?? bridgeParams.auroraEvmAccount, status: status.IN_PROGRESS, lockHashes: [lockTxHash], lockReceipts: [receipt] @@ -299,7 +301,7 @@ export async function recover ( * @param params.options.erc20LockerAddress Rainbow bridge ERC-20 token locker address. * @param params.options.erc20LockerAbi Rainbow bridge ERC-20 token locker abi. * @param params.options.erc20Abi Standard ERC-20 token abi. - * @param options.auroraEvmAccount Aurora account on NEAR. + * @param params.options.auroraEvmAccount Aurora Cloud silo account on NEAR. * @param params.options.signer Ethers signer to use. * @returns The created transfer object. */ @@ -333,7 +335,7 @@ export async function initiate ( const sender = options.sender ?? (await signer.getAddress()).toLowerCase() // various attributes stored as arrays, to keep history of retries - let transfer = { + let transfer: Transfer = { ...transferDraft, id: Math.random().toString().slice(2), @@ -344,6 +346,7 @@ export async function initiate ( sender, sourceToken: erc20Address, sourceTokenName, + auroraEvmAccount: options.auroraEvmAccount ?? getBridgeParams().auroraEvmAccount, symbol, decimals } diff --git a/packages/aurora-ether/src/bridged-ether/sendToEthereum/index.ts b/packages/aurora-ether/src/bridged-ether/sendToEthereum/index.ts index 1a177e4b..317cb307 100644 --- a/packages/aurora-ether/src/bridged-ether/sendToEthereum/index.ts +++ b/packages/aurora-ether/src/bridged-ether/sendToEthereum/index.ts @@ -1,19 +1,21 @@ -import { borshifyOutcomeProof, nearOnEthSyncHeight, findNearProof, findFinalizationTxOnEthereum } from '@near-eth/utils' +import { + borshifyOutcomeProof, + nearOnEthSyncHeight, + findNearProof, + findFinalizationTxOnEthereum, + parseETHBurnReceipt, + parseNep141BurnReceipt +} from '@near-eth/utils' import { ethers } from 'ethers' import bs58 from 'bs58' import { Account, providers as najProviders } from 'near-api-js' -import BN from 'bn.js' -import { - deserialize as deserializeBorsh -} from 'near-api-js/lib/utils/serialize' -import { FinalExecutionOutcome } from 'near-api-js/lib/providers' import { track } from '@near-eth/client' import { stepsFor } from '@near-eth/client/dist/i18nHelpers' import * as status from '@near-eth/client/dist/statuses' import { TransferStatus, TransactionInfo } from '@near-eth/client/dist/types' import { getSignerProvider, - getAuroraProvider, + getAuroraCloudProvider, getEthProvider, getNearProvider, formatLargeNum, @@ -66,6 +68,8 @@ export interface Transfer extends TransferDraft, TransactionInfo { checkSyncInterval?: number nextCheckSyncTimestamp?: Date proof?: Uint8Array + auroraEvmAccount?: string + auroraChainId?: string } export interface TransferOptions { @@ -74,12 +78,14 @@ export interface TransferOptions { etherCustodianAbi?: string sendToEthereumSyncInterval?: number ethChainId?: number + auroraChainId?: string nearAccount?: Account nearProvider?: najProviders.Provider ethClientAddress?: string ethClientAbi?: string auroraProvider?: ethers.providers.JsonRpcProvider auroraEvmAccount?: string + symbol?: string } const transferDraft: TransferDraft = { @@ -184,50 +190,6 @@ export async function checkStatus (transfer: Transfer): Promise { } } -/** - * Parse the burn receipt id and block height needed to complete - * the step BURN - */ -export async function parseBurnReceipt ( - nearBurnTx: FinalExecutionOutcome, - nearProvider: najProviders.Provider -): Promise<{id: string, blockHeight: number, blockTimestamp: number, event: { amount: string, recipient: string, etherCustodian: string }}> { - const bridgeReceipt: any = nearBurnTx.receipts_outcome[1] - if (!bridgeReceipt) { - throw new Error(`Failed to parse bridge receipt for ${JSON.stringify(nearBurnTx)}`) - } - const successValue = bridgeReceipt.outcome.status.SuccessValue - // eslint-disable-next-line @typescript-eslint/no-extraneous-class - class BurnEvent { - constructor (args: any) { - Object.assign(this, args) - } - } - const SCHEMA = new Map([ - [BurnEvent, { - kind: 'struct', - fields: [ - ['amount', 'u128'], - ['recipient_id', [20]], - ['eth_custodian_address', [20]] - ] - }] - ]) - const rawEvent = deserializeBorsh( - SCHEMA, BurnEvent, Buffer.from(successValue, 'base64') - ) as { amount: BN, recipient_id: Uint8Array, eth_custodian_address: Uint8Array} - const event = { - amount: rawEvent.amount.toString(), - recipient: '0x' + Buffer.from(rawEvent.recipient_id).toString('hex'), - etherCustodian: '0x' + Buffer.from(rawEvent.eth_custodian_address).toString('hex') - } - - const receiptBlock = await nearProvider.block({ blockId: bridgeReceipt.block_hash }) - const blockHeight = Number(receiptBlock.header.height) - const blockTimestamp = Number(receiptBlock.header.timestamp) - return { id: bridgeReceipt.id, blockHeight, blockTimestamp, event } -} - export async function findAllTransactions ( { fromBlock, toBlock, sender, options }: { fromBlock: number | string @@ -236,12 +198,13 @@ export async function findAllTransactions ( options?: { provider?: ethers.providers.Provider etherExitToEthereumPrecompile?: string + auroraEvmAccount?: string } } ): Promise { options = options ?? {} const bridgeParams = getBridgeParams() - const provider = options.provider ?? getAuroraProvider() + const provider = options.provider ?? getAuroraCloudProvider({ auroraEvmAccount: options?.auroraEvmAccount }) const filter = { address: options.etherExitToEthereumPrecompile ?? bridgeParams.etherExitToEthereumPrecompile, @@ -279,7 +242,9 @@ export async function findAllTransfers ( export async function recover ( burnTxHash: string, sender: string = 'todo', - options?: TransferOptions + options?: TransferOptions & { + nep141Factory?: string + } ): Promise { options = options ?? {} const bridgeParams = getBridgeParams() @@ -288,7 +253,7 @@ export async function recover ( options.nearAccount?.connection.provider ?? getNearProvider() - const auroraProvider = options.auroraProvider ?? getAuroraProvider() + const auroraProvider = options.auroraProvider ?? getAuroraCloudProvider({ auroraEvmAccount: options?.auroraEvmAccount }) // Ethers formats the receipts and removes nearTransactionHash const auroraBurnReceipt = await auroraProvider.send('eth_getTransactionReceipt', [burnTxHash]) const decodedTxHash = Buffer.from(auroraBurnReceipt.nearTransactionHash.slice(2), 'hex') @@ -312,17 +277,32 @@ export async function recover ( throw new Error(`Withdraw transaction failed: ${nearBurnTxHash}`) } - const nearBurnReceipt = await parseBurnReceipt(burnTx, nearProvider) + const auroraEvmAccount = options.auroraEvmAccount ?? bridgeParams.auroraEvmAccount - const { amount, recipient, etherCustodian } = nearBurnReceipt.event - const etherCustodianAddress: string = options.etherCustodianAddress ?? bridgeParams.etherCustodianAddress - if (etherCustodian.toLowerCase() !== etherCustodianAddress.toLowerCase()) { - throw new Error( - `Unexpected ether custodian: got ${etherCustodian}, - expected ${etherCustodianAddress}` - ) + let nearBurnReceipt + let amount + let recipient + if (options.symbol && options.symbol !== 'ETH') { + // Withdraw native currency from a silo which doesn't use ETH as native currency + const nep141Factory = options.nep141Factory ?? getBridgeParams().nep141Factory + nearBurnReceipt = await parseNep141BurnReceipt(burnTx, nep141Factory, nearProvider) + amount = nearBurnReceipt.event.amount + recipient = nearBurnReceipt.event.recipient + } else { + nearBurnReceipt = await parseETHBurnReceipt(burnTx, auroraEvmAccount, nearProvider) + amount = nearBurnReceipt.event.amount + recipient = nearBurnReceipt.event.recipient + const etherCustodian: string = nearBurnReceipt.event.etherCustodian + const etherCustodianAddress: string = options.etherCustodianAddress ?? bridgeParams.etherCustodianAddress + if (etherCustodian.toLowerCase() !== etherCustodianAddress.toLowerCase()) { + throw new Error( + `Unexpected ether custodian: got ${etherCustodian}, + expected ${etherCustodianAddress}` + ) + } } - const symbol = 'ETH' + // A silo might not use ETH as its base currency. + const symbol = options.symbol ?? 'ETH' const destinationTokenName = symbol const decimals = 18 const sourceTokenName = 'a' + symbol @@ -343,6 +323,8 @@ export async function recover ( sourceTokenName, symbol, decimals, + auroraEvmAccount, + auroraChainId: options.auroraChainId ?? bridgeParams.auroraChainId, burnHashes: [burnTxHash], nearBurnHashes: [nearBurnTxHash], nearBurnReceiptIds: [nearBurnReceipt.id], @@ -367,6 +349,7 @@ export async function recover ( * @param params.options.auroraChainId Aurora chain id of the bridge. * @param params.options.provider Ethereum provider to use. * @param params.options.etherExitToEthereumPrecompile Aurora ether exit to Ethereum precompile address. + * @param params.options.auroraEvmAccount NEAR account of the silo to witdraw from. * @param params.options.signer Ethers signer to use. * @returns The created transfer object. */ @@ -381,6 +364,7 @@ export async function initiate ( auroraChainId?: number provider?: ethers.providers.JsonRpcProvider etherExitToEthereumPrecompile?: string + auroraEvmAccount?: string signer?: ethers.Signer } } @@ -395,9 +379,10 @@ export async function initiate ( const provider = options.provider ?? getSignerProvider() const signer = options.signer ?? provider.getSigner() const sender = options.sender ?? (await signer.getAddress()).toLowerCase() + const bridgeParams = getBridgeParams() // various attributes stored as arrays, to keep history of retries - let transfer = { + let transfer: Transfer = { ...transferDraft, id: Math.random().toString().slice(2), @@ -408,6 +393,8 @@ export async function initiate ( sender, sourceToken, sourceTokenName, + auroraEvmAccount: options.auroraEvmAccount ?? bridgeParams.auroraEvmAccount, + auroraChainId: options.auroraChainId ?? bridgeParams.auroraChainId, symbol, decimals } @@ -495,16 +482,17 @@ export async function checkBurn ( auroraRelayerAccount?: string nearAccount?: Account nearProvider?: najProviders.Provider + nep141Factory?: string } ): Promise { options = options ?? {} const bridgeParams = getBridgeParams() - const provider = options.provider ?? getAuroraProvider() + const provider = options.provider ?? getAuroraCloudProvider({ auroraEvmAccount: transfer.auroraEvmAccount }) const burnHash = last(transfer.burnHashes) const ethChainId: number = (await provider.getNetwork()).chainId - const expectedChainId: number = options.auroraChainId ?? bridgeParams.auroraChainId + const expectedChainId: number = options.auroraChainId ?? transfer.auroraChainId ?? bridgeParams.auroraChainId if (ethChainId !== expectedChainId) { throw new Error( `Wrong aurora network for checkBurn, expected: ${expectedChainId}, got: ${ethChainId}` @@ -591,7 +579,13 @@ export async function checkBurn ( let nearBurnReceipt try { - nearBurnReceipt = await parseBurnReceipt(nearBurnTx, nearProvider) + if (transfer.symbol !== 'ETH') { + // Withdraw native currency from a silo which doesn't use ETH as native currency + const nep141Factory = options.nep141Factory ?? getBridgeParams().nep141Factory + nearBurnReceipt = await parseNep141BurnReceipt(nearBurnTx, nep141Factory, nearProvider) + } else { + nearBurnReceipt = await parseETHBurnReceipt(nearBurnTx, transfer.auroraEvmAccount ?? 'aurora', nearProvider) + } } catch (e) { if (e instanceof TransferError) { return { @@ -699,7 +693,12 @@ export async function checkSync ( if (nearOnEthClientBlockHeight > burnBlockHeight) { proof = await findNearProof( last(transfer.nearBurnReceiptIds), - options.auroraEvmAccount ?? bridgeParams.auroraEvmAccount, + // NOTE: options.auroraEvmAccount cannot be used because checkSync can be called by recover using a different silo's auroraEvmAccount. + // NOTE: If another token than ETH is being transfered with @near-eth/aurora-ether, + // it means that ETH is not the silo's native currency + transfer.symbol !== 'ETH' + ? bridgeParams.nep141Factory + : bridgeParams.auroraEvmAccount, nearOnEthClientBlockHeight, nearProvider, provider, diff --git a/packages/aurora-ether/src/natural-ether/sendToAurora/index.ts b/packages/aurora-ether/src/natural-ether/sendToAurora/index.ts index 8c518270..71631420 100644 --- a/packages/aurora-ether/src/natural-ether/sendToAurora/index.ts +++ b/packages/aurora-ether/src/natural-ether/sendToAurora/index.ts @@ -45,6 +45,7 @@ export interface Transfer extends TransferDraft, TransactionInfo { checkSyncInterval?: number nextCheckSyncTimestamp?: Date proof?: Uint8Array + auroraEvmAccount?: string } export interface TransferOptions { @@ -253,6 +254,7 @@ export async function recover ( sourceTokenName, symbol, decimals, + auroraEvmAccount: options.auroraEvmAccount ?? bridgeParams.auroraEvmAccount, status: status.IN_PROGRESS, lockHashes: [lockTxHash], lockReceipts: [receipt] @@ -276,6 +278,7 @@ export async function recover ( * @param params.options.provider Ethereum provider to use. * @param params.options.etherCustodianAddress Rainbow bridge ether custodian address. * @param params.options.etherCustodianAbi Rainbow bridge ether custodian abi. + * @param params.options.auroraEvmAccount Aurora Cloud silo account on NEAR. * @param params.options.signer Ethers signer to use. * @returns The created transfer object. */ @@ -291,6 +294,7 @@ export async function initiate ( provider?: ethers.providers.JsonRpcProvider etherCustodianAddress?: string etherCustodianAbi?: string + auroraEvmAccount?: string signer?: ethers.Signer } } @@ -307,7 +311,7 @@ export async function initiate ( const sender = options.sender ?? (await signer.getAddress()).toLowerCase() // various attributes stored as arrays, to keep history of retries - let transfer = { + let transfer: Transfer = { ...transferDraft, id: Math.random().toString().slice(2), @@ -318,6 +322,7 @@ export async function initiate ( sender, sourceToken, sourceTokenName, + auroraEvmAccount: options.auroraEvmAccount ?? getBridgeParams().auroraEvmAccount, symbol, decimals } @@ -342,6 +347,7 @@ export async function lock ( ethChainId?: number etherCustodianAddress?: string etherCustodianAbi?: string + auroraEvmAccount?: string signer?: ethers.Signer } ): Promise { @@ -367,8 +373,10 @@ export async function lock ( // If this tx is dropped and replaced, lower the search boundary // in case there was a reorg. const safeReorgHeight = await provider.getBlockNumber() - 20 - const pendingLockTx = await ethTokenLocker.depositToEVM( - transfer.recipient.slice(2).toLowerCase(), 0, { value: transfer.amount } + + const auroraEvmAccount: string = options.auroraEvmAccount ?? bridgeParams.auroraEvmAccount ?? 'aurora' + const pendingLockTx = await ethTokenLocker.depositToNear( + `${auroraEvmAccount}:${transfer.recipient.slice(2).toLowerCase()}`, 0, { value: transfer.amount } ) return { @@ -510,7 +518,8 @@ export async function checkSync ( ) const result = await nearProvider.query({ request_type: 'call_function', - account_id: options.auroraEvmAccount ?? bridgeParams.auroraEvmAccount, + // NOTE: options.auroraEvmAccount cannot be used because checkSync can be called by recover using a different silo's auroraEvmAccount. + account_id: bridgeParams.auroraEvmAccount, method_name: 'is_used_proof', args_base64: Buffer.from(proof).toString('base64'), finality: 'optimistic' diff --git a/packages/aurora-nep141/src/bridged-erc20/getBalance.ts b/packages/aurora-nep141/src/bridged-erc20/getBalance.ts index 1dba7531..104563d1 100644 --- a/packages/aurora-nep141/src/bridged-erc20/getBalance.ts +++ b/packages/aurora-nep141/src/bridged-erc20/getBalance.ts @@ -1,5 +1,5 @@ import { ethers } from 'ethers' -import { getAuroraProvider, getBridgeParams } from '@near-eth/client/dist/utils' +import { getAuroraCloudProvider, getBridgeParams } from '@near-eth/client/dist/utils' import { erc20 } from '@near-eth/utils' export default async function getBalance ( @@ -9,12 +9,13 @@ export default async function getBalance ( options?: { provider?: ethers.providers.Provider auroraErc20Abi?: string + auroraEvmAccount?: string } } ): Promise { options = options ?? {} const bridgeParams = getBridgeParams() - const provider = options.provider ?? getAuroraProvider() + const provider = options.provider ?? getAuroraCloudProvider({ auroraEvmAccount: options.auroraEvmAccount }) const erc20Abi = options.auroraErc20Abi ?? bridgeParams.auroraErc20Abi const balance = await erc20.getBalance({ erc20Address, owner, provider, erc20Abi }) diff --git a/packages/aurora-nep141/src/bridged-erc20/sendToNear/index.ts b/packages/aurora-nep141/src/bridged-erc20/sendToNear/index.ts index d598bec2..b476b5c2 100644 --- a/packages/aurora-nep141/src/bridged-erc20/sendToNear/index.ts +++ b/packages/aurora-nep141/src/bridged-erc20/sendToNear/index.ts @@ -2,7 +2,7 @@ import BN from 'bn.js' import { getNearWallet, getNearAccountId } from '@near-eth/client/dist/utils' import { ethers } from 'ethers' import { Account, providers as najProviders } from 'near-api-js' -import { getAuroraProvider, getSignerProvider, getBridgeParams, track } from '@near-eth/client' +import { getAuroraCloudProvider, getSignerProvider, getBridgeParams, track } from '@near-eth/client' import { TransactionInfo, TransferStatus } from '@near-eth/client/dist/types' import * as status from '@near-eth/client/dist/statuses' import { findReplacementTx, TxValidationError } from 'find-replacement-tx' @@ -31,6 +31,8 @@ export interface Transfer extends TransactionInfo, TransferDraft { sourceTokenName: string symbol: string startTime?: string + auroraEvmAccount?: string + auroraChainId?: string } const transferDraft: TransferDraft = { @@ -116,7 +118,7 @@ export async function findAllTransfers ( ): Promise { options = options ?? {} const bridgeParams = getBridgeParams() - const provider = options.provider ?? getAuroraProvider() + const provider = options.provider ?? getAuroraCloudProvider({ auroraEvmAccount: options?.auroraEvmAccount }) const auroraErc20Address = options.auroraErc20Address ?? await getAuroraErc20Address( { nep141Address, options } ) as string @@ -133,7 +135,7 @@ export async function findAllTransfers ( })) // Keep only transfers from Aurora to NEAR. const transferReceipts = receipts.filter((receipt) => receipt.logs.find( - (log) => log.topics[0] === EXIT_TO_NEAR_SIGNATURE + log => log.topics[0] === EXIT_TO_NEAR_SIGNATURE )) let metadata = { symbol: '', decimals: 0 } if (!options.symbol || !options.decimals) { @@ -160,6 +162,7 @@ export async function findAllTransfers ( amount, decimals, symbol, + auroraEvmAccount: options?.auroraEvmAccount ?? bridgeParams.auroraEvmAccount, sourceToken: auroraErc20Address, sourceTokenName, destinationTokenName, @@ -183,10 +186,11 @@ export async function recover ( nearProvider?: najProviders.Provider decimals?: number symbol?: string + auroraChainId?: string } ): Promise { options = options ?? {} - const provider = options.provider ?? getAuroraProvider() + const provider = options.provider ?? getAuroraCloudProvider({ auroraEvmAccount: options?.auroraEvmAccount }) const receipt = await provider.getTransactionReceipt(burnTxHash) const exitLog: ethers.providers.Log = receipt.logs.find(log => log.topics[0] === EXIT_TO_NEAR_SIGNATURE)! @@ -206,6 +210,7 @@ export async function recover ( const destinationTokenName = symbol const decimals = options.decimals ?? metadata.decimals const txBlock = await provider.getBlock(receipt.blockHash) + const bridgeParams = getBridgeParams() const transfer = { id: Math.random().toString().slice(2), @@ -217,6 +222,8 @@ export async function recover ( amount, decimals, symbol, + auroraEvmAccount: options.auroraEvmAccount ?? bridgeParams.auroraEvmAccount, + auroraChainId: options.auroraChainId ?? bridgeParams.auroraChainId, sourceToken: auroraErc20Address, sourceTokenName, destinationTokenName, @@ -237,9 +244,9 @@ export async function checkBurn ( ): Promise { options = options ?? {} const bridgeParams = getBridgeParams() - const provider = options.provider ?? getAuroraProvider() + const provider = options.provider ?? getAuroraCloudProvider({ auroraEvmAccount: transfer.auroraEvmAccount }) const ethChainId: number = (await provider.getNetwork()).chainId - const expectedChainId: number = options.auroraChainId ?? bridgeParams.auroraChainId + const expectedChainId: number = options.auroraChainId ?? transfer.auroraChainId ?? bridgeParams.auroraChainId if (ethChainId !== expectedChainId) { throw new Error( `Wrong aurora network for checkBurn, expected: ${expectedChainId}, got: ${ethChainId}` @@ -357,7 +364,7 @@ export async function sendToNear ( symbol?: string decimals?: number sender?: string - ethChainId?: number + auroraChainId?: number provider?: ethers.providers.JsonRpcProvider auroraErc20Abi?: string auroraErc20Address?: string @@ -365,6 +372,7 @@ export async function sendToNear ( nearAccount?: Account nearProvider?: najProviders.Provider auroraEvmAccount?: string + unwrapWNear?: boolean } } ): Promise { @@ -382,6 +390,7 @@ export async function sendToNear ( const sender = options.sender ?? (await signer.getAddress()).toLowerCase() const auroraErc20Address = options.auroraErc20Address ?? await getAuroraErc20Address({ nep141Address, options }) if (!auroraErc20Address) throw new Error(`Token not bridged: ${nep141Address}`) + const bridgeParams = getBridgeParams() let transfer: Transfer = { ...transferDraft, @@ -390,6 +399,8 @@ export async function sendToNear ( amount: amount.toString(), decimals, symbol, + auroraEvmAccount: options.auroraEvmAccount ?? bridgeParams.auroraEvmAccount, + auroraChainId: options.auroraChainId ?? bridgeParams.auroraChainId, sourceToken: auroraErc20Address, sourceTokenName, destinationTokenName, @@ -410,6 +421,7 @@ export async function burn ( auroraChainId?: number auroraErc20Abi?: string signer?: ethers.Signer + unwrapWNear?: boolean } ): Promise { options = options ?? {} @@ -432,7 +444,7 @@ export async function burn ( ) const safeReorgHeight = await provider.getBlockNumber() - 20 const tx = await erc20Contract.withdrawToNear( - Buffer.from(transfer.recipient), + Buffer.from(options.unwrapWNear ? transfer.recipient + ':unwrap' : transfer.recipient), transfer.amount, { gasLimit: 100000 } ) diff --git a/packages/aurora-nep141/src/bridged-ether/sendToNear/index.ts b/packages/aurora-nep141/src/bridged-ether/sendToNear/index.ts index bf6a0bc8..240dc083 100644 --- a/packages/aurora-nep141/src/bridged-ether/sendToNear/index.ts +++ b/packages/aurora-nep141/src/bridged-ether/sendToNear/index.ts @@ -1,5 +1,5 @@ import { ethers } from 'ethers' -import { getAuroraProvider, getSignerProvider, getBridgeParams, track } from '@near-eth/client' +import { getAuroraCloudProvider, getSignerProvider, getBridgeParams, track } from '@near-eth/client' import { TransactionInfo, TransferStatus } from '@near-eth/client/dist/types' import * as status from '@near-eth/client/dist/statuses' import { findReplacementTx, TxValidationError } from 'find-replacement-tx' @@ -26,6 +26,8 @@ export interface Transfer extends TransactionInfo, TransferDraft { sourceTokenName: string symbol: string startTime?: string + auroraEvmAccount?: string + auroraChainId?: string } const transferDraft: TransferDraft = { @@ -99,12 +101,13 @@ export async function findAllTransfers ( options?: { provider?: ethers.providers.Provider etherExitToNearPrecompile?: string + auroraEvmAccount?: string } } ): Promise { options = options ?? {} const bridgeParams = getBridgeParams() - const provider = options.provider ?? getAuroraProvider() + const provider = options.provider ?? getAuroraCloudProvider({ auroraEvmAccount: options?.auroraEvmAccount }) const filter = { address: options.etherExitToNearPrecompile ?? bridgeParams.etherExitToNearPrecompile, @@ -131,6 +134,7 @@ export async function findAllTransfers ( sourceTokenName: 'ETH', destinationTokenName: 'ETH', sender, + auroraEvmAccount: options?.auroraEvmAccount ?? bridgeParams.auroraEvmAccount, recipient: `NEAR account hash: ${recipientHash}`, burnHashes: [log.transactionHash], burnReceipts: [] @@ -145,10 +149,14 @@ export async function recover ( options?: { provider?: ethers.providers.Provider etherExitToNearPrecompile?: string + decimals?: number + symbol?: string + auroraEvmAccount?: string + auroraChainId?: number } ): Promise { options = options ?? {} - const provider = options.provider ?? getAuroraProvider() + const provider = options.provider ?? getAuroraCloudProvider({ auroraEvmAccount: options.auroraEvmAccount }) const bridgeParams = getBridgeParams() const receipt = await provider.getTransactionReceipt(burnTxHash) @@ -164,6 +172,12 @@ export async function recover ( const txBlock = await provider.getBlock(receipt.blockHash) + const symbol = options.symbol ?? 'ETH' + const destinationTokenName = symbol + const sourceTokenName = 'a' + symbol + const sourceToken = symbol + const decimals = options.decimals ?? 18 + const transfer = { id: Math.random().toString().slice(2), startTime: new Date(txBlock.timestamp * 1000).toISOString(), @@ -172,12 +186,14 @@ export async function recover ( completedStep: BURN, errors: [], amount, - decimals: 18, - symbol: 'ETH', - sourceToken: 'ETH', - sourceTokenName: 'ETH', - destinationTokenName: 'ETH', + decimals, + symbol, + sourceToken, + sourceTokenName, + destinationTokenName, sender, + auroraEvmAccount: options.auroraEvmAccount ?? bridgeParams.auroraEvmAccount, + auroraChainId: options.auroraChainId ?? bridgeParams.auroraChainId, recipient: `NEAR account hash: ${recipientHash}`, burnHashes: [receipt.transactionHash], burnReceipts: [] @@ -194,9 +210,9 @@ export async function checkBurn ( ): Promise { options = options ?? {} const bridgeParams = getBridgeParams() - const provider = options.provider ?? getAuroraProvider() + const provider = options.provider ?? getAuroraCloudProvider({ auroraEvmAccount: transfer.auroraEvmAccount }) const ethChainId: number = (await provider.getNetwork()).chainId - const expectedChainId: number = options.auroraChainId ?? bridgeParams.auroraChainId + const expectedChainId: number = options.auroraChainId ?? transfer.auroraChainId ?? bridgeParams.auroraChainId if (ethChainId !== expectedChainId) { throw new Error( `Wrong aurora network for checkBurn, expected: ${expectedChainId}, got: ${ethChainId}` @@ -265,10 +281,11 @@ export async function sendToNear ( symbol?: string decimals?: number sender?: string - ethChainId?: number + auroraChainId?: number provider?: ethers.providers.JsonRpcProvider signer?: ethers.Signer etherExitToNearPrecompile?: string + auroraEvmAccount?: string } } ): Promise { @@ -281,6 +298,7 @@ export async function sendToNear ( const decimals = options.decimals ?? 18 const signer = options.signer ?? provider.getSigner() const sender = options.sender ?? (await signer.getAddress()).toLowerCase() + const bridgeParams = getBridgeParams() let transfer: Transfer = { ...transferDraft, @@ -291,6 +309,8 @@ export async function sendToNear ( sourceToken, sourceTokenName, destinationTokenName, + auroraEvmAccount: options.auroraEvmAccount ?? bridgeParams.auroraEvmAccount, + auroraChainId: options.auroraChainId ?? bridgeParams.auroraChainId, decimals, sender, recipient diff --git a/packages/aurora-nep141/src/natural-nep141/sendToAurora/index.ts b/packages/aurora-nep141/src/natural-nep141/sendToAurora/index.ts index 0b58b0e1..cd5880ef 100644 --- a/packages/aurora-nep141/src/natural-nep141/sendToAurora/index.ts +++ b/packages/aurora-nep141/src/natural-nep141/sendToAurora/index.ts @@ -29,6 +29,7 @@ export interface Transfer extends TransactionInfo, TransferDraft { sender: string sourceTokenName: string symbol: string + auroraEvmAccount?: string } const transferDraft: TransferDraft = { @@ -194,6 +195,7 @@ export async function findAllTransfers ( amount, decimals, symbol, + auroraEvmAccount, sourceToken: nep141Address, sourceTokenName: metadata.symbol, destinationTokenName: 'a' + metadata.symbol, @@ -271,6 +273,7 @@ export async function recover ( amount: argsJson.amount, decimals, symbol, + auroraEvmAccount, sourceToken: nep141Address, sourceTokenName: metadata.symbol, destinationTokenName: 'a' + metadata.symbol, @@ -381,13 +384,14 @@ export async function sendToAurora ( const sourceToken = nep141Address const sender = options.sender ?? await getNearAccountId() - let transfer = { + let transfer: Transfer = { ...transferDraft, id: Math.random().toString().slice(2), startTime: new Date().toISOString(), amount: amount.toString(), decimals, symbol, + auroraEvmAccount: options.auroraEvmAccount ?? getBridgeParams().auroraEvmAccount, sourceToken, sourceTokenName, destinationTokenName, @@ -510,6 +514,7 @@ export async function wrapAndSendNearToAurora ( amount: amount.toString(), decimals: 24, symbol, + auroraEvmAccount: options.auroraEvmAccount ?? getBridgeParams().auroraEvmAccount, sourceToken, sourceTokenName, destinationTokenName, diff --git a/packages/client/src/index.ts b/packages/client/src/index.ts index 18cc2735..5fa415ad 100644 --- a/packages/client/src/index.ts +++ b/packages/client/src/index.ts @@ -13,6 +13,7 @@ import { export { onChange } from './storage' export { getAuroraProvider, + getAuroraCloudProvider, getBridgeParams, getEthProvider, getNearAccount, @@ -21,6 +22,7 @@ export { getNearAccountId, getSignerProvider, setAuroraProvider, + setAuroraCloudProviders, setBridgeParams, setEthProvider, setNearConnection, diff --git a/packages/client/src/utils.ts b/packages/client/src/utils.ts index e7614c55..3c239e1b 100644 --- a/packages/client/src/utils.ts +++ b/packages/client/src/utils.ts @@ -9,6 +9,9 @@ import { providers as ethersProviders } from 'ethers' let ethProvider: ethersProviders.JsonRpcProvider let nearConnection: WalletConnection let auroraProvider: ethersProviders.JsonRpcProvider +type AuroraEvmAccount = string +interface AuroraCloudProviders { [key: AuroraEvmAccount]: ethersProviders.JsonRpcProvider } +let auroraCloudProviders: AuroraCloudProviders let signerProvider: ethersProviders.JsonRpcProvider | ethersProviders.Web3Provider let nearProvider: najProviders.Provider let nearWallet: NearWalletBehaviour @@ -76,6 +79,30 @@ export function setAuroraProvider ( return auroraProvider } +/** + * Set cloudProviders + * + * This must be called by apps that use @near-eth/client before performing any + * transfer operations with @near-eth/client itself or with connector libraries + * such as @near-eth/aurora-erc20. + * + * Example: + * + * import { ethers } from 'ethers' + * import { setAuroraCloudProviders } from '@near-eth/client' + * setAuroraCloudProviders({ "example-silo.near": new ethers.providers.JsonRpcProvider(url) }) + * + * @param cloudProviders Object of Aurora evm engine accounts to their respective provider. + * + * @returns `auroraCloudProviders` + */ +export function setAuroraCloudProviders ( + cloudProviders: AuroraCloudProviders +): AuroraCloudProviders { + auroraCloudProviders = cloudProviders + return auroraCloudProviders +} + /** * Set signerProvider * @@ -201,6 +228,19 @@ export function getAuroraProvider (): ethersProviders.JsonRpcProvider { return auroraProvider } +export function getAuroraCloudProvider ( + { auroraEvmAccount }: { auroraEvmAccount?: AuroraEvmAccount } +): ethersProviders.JsonRpcProvider { + if (!auroraEvmAccount || auroraEvmAccount === 'aurora') return getAuroraProvider() + const cloudProvider = auroraCloudProviders[auroraEvmAccount] + if (!cloudProvider) { + throw new Error( + 'Must `setAuroraCloudProviders({ "example-silo.near": new ethers.providers.JsonRpcProvider(url) })` prior to calling getAuroraCloudProvider' + ) + } + return cloudProvider +} + export function getSignerProvider (): ethersProviders.JsonRpcProvider { return signerProvider } diff --git a/packages/near-ether/src/bridged-ether/sendToEthereum/index.ts b/packages/near-ether/src/bridged-ether/sendToEthereum/index.ts index e965cadf..700f6500 100644 --- a/packages/near-ether/src/bridged-ether/sendToEthereum/index.ts +++ b/packages/near-ether/src/bridged-ether/sendToEthereum/index.ts @@ -2,16 +2,20 @@ import BN from 'bn.js' import bs58 from 'bs58' import { ethers } from 'ethers' import { Account, utils, providers as najProviders } from 'near-api-js' -import { FinalExecutionOutcome } from 'near-api-js/lib/providers' -import { - deserialize as deserializeBorsh, - serialize as serializeBorsh -} from 'near-api-js/lib/utils/serialize' +import { serialize as serializeBorsh } from 'near-api-js/lib/utils/serialize' import * as status from '@near-eth/client/dist/statuses' import { stepsFor } from '@near-eth/client/dist/i18nHelpers' import { TransferStatus, TransactionInfo } from '@near-eth/client/dist/types' import { track, untrack } from '@near-eth/client' -import { borshifyOutcomeProof, urlParams, nearOnEthSyncHeight, findNearProof, buildIndexerTxQuery, findFinalizationTxOnEthereum } from '@near-eth/utils' +import { + borshifyOutcomeProof, + urlParams, + nearOnEthSyncHeight, + findNearProof, + buildIndexerTxQuery, + findFinalizationTxOnEthereum, + parseETHBurnReceipt +} from '@near-eth/utils' import { findReplacementTx, TxValidationError } from 'find-replacement-tx' import { getEthProvider, getNearWallet, getNearProvider, getNearAccountId, formatLargeNum, getSignerProvider, getBridgeParams } from '@near-eth/client/dist/utils' @@ -284,9 +288,12 @@ export async function recover ( } const auroraEvmAccount = options.auroraEvmAccount ?? bridgeParams.auroraEvmAccount - const withdrawReceipt = await parseWithdrawReceipt(burnTx, auroraEvmAccount, nearProvider) - const { amount, recipient, etherCustodian } = withdrawReceipt.event + const withdrawReceipt = await parseETHBurnReceipt(burnTx, auroraEvmAccount, nearProvider) + const amount = withdrawReceipt.event.amount + const recipient = withdrawReceipt.event.recipient + const etherCustodian: string = withdrawReceipt.event.etherCustodian + const etherCustodianAddress: string = options.etherCustodianAddress ?? bridgeParams.etherCustodianAddress if (etherCustodian.toLowerCase() !== etherCustodianAddress.toLowerCase()) { throw new Error( @@ -325,55 +332,6 @@ export async function recover ( return await checkSync(transfer, options) } -/** - * Parse the burn receipt id and block height needed to complete - * the step BURN - * @param burnTx - * @param auroraEvmAccount - * @param nearProvider - */ -export async function parseWithdrawReceipt ( - burnTx: FinalExecutionOutcome, - auroraEvmAccount: string, - nearProvider: najProviders.Provider -): Promise<{id: string, blockHeight: number, blockTimestamp: number, event: { amount: string, recipient: string, etherCustodian: string }}> { - // @ts-expect-error - const bridgeReceipt: any = burnTx.receipts_outcome.find(r => r.outcome.executor_id === auroraEvmAccount) - if (!bridgeReceipt) { - throw new Error(`Failed to parse bridge receipt for ${JSON.stringify(burnTx)}`) - } - const successValue = bridgeReceipt.outcome.status.SuccessValue - // eslint-disable-next-line @typescript-eslint/no-extraneous-class - class WithdrawEvent { - constructor (args: any) { - Object.assign(this, args) - } - } - const SCHEMA = new Map([ - [WithdrawEvent, { - kind: 'struct', - fields: [ - ['amount', 'u128'], - ['recipient_id', [20]], - ['eth_custodian_address', [20]] - ] - }] - ]) - const rawEvent = deserializeBorsh( - SCHEMA, WithdrawEvent, Buffer.from(successValue, 'base64') - ) as { amount: BN, recipient_id: Uint8Array, eth_custodian_address: Uint8Array} - const event = { - amount: rawEvent.amount.toString(), - recipient: '0x' + Buffer.from(rawEvent.recipient_id).toString('hex'), - etherCustodian: '0x' + Buffer.from(rawEvent.eth_custodian_address).toString('hex') - } - - const receiptBlock = await nearProvider.block({ blockId: bridgeReceipt.block_hash }) - const blockHeight = Number(receiptBlock.header.height) - const blockTimestamp = Number(receiptBlock.header.timestamp) - return { id: bridgeReceipt.id, blockHeight, blockTimestamp, event } -} - /** * Initiate a transfer from NEAR to Ethereum by burning nETH tokens. * @param params Uses Named Arguments pattern, please pass arguments as object @@ -637,7 +595,7 @@ export async function checkBurn ( let withdrawReceipt try { const auroraEvmAccount = options.auroraEvmAccount ?? getBridgeParams().auroraEvmAccount - withdrawReceipt = await parseWithdrawReceipt(burnTx, auroraEvmAccount, nearProvider) + withdrawReceipt = await parseETHBurnReceipt(burnTx, auroraEvmAccount, nearProvider) } catch (e) { if (e instanceof TransferError) { if (clearParams) urlParams.clear(...clearParams) diff --git a/packages/near-ether/src/natural-near/sendToEthereum/index.ts b/packages/near-ether/src/natural-near/sendToEthereum/index.ts index c7235106..f924520e 100644 --- a/packages/near-ether/src/natural-near/sendToEthereum/index.ts +++ b/packages/near-ether/src/natural-near/sendToEthereum/index.ts @@ -2,15 +2,19 @@ import BN from 'bn.js' import bs58 from 'bs58' import { ethers } from 'ethers' import { Account, utils, providers as najProviders } from 'near-api-js' -import { FinalExecutionOutcome } from 'near-api-js/lib/providers' -import { - deserialize as deserializeBorsh -} from 'near-api-js/lib/utils/serialize' import * as status from '@near-eth/client/dist/statuses' import { stepsFor } from '@near-eth/client/dist/i18nHelpers' import { TransferStatus, TransactionInfo } from '@near-eth/client/dist/types' import { track, untrack } from '@near-eth/client' -import { borshifyOutcomeProof, urlParams, nearOnEthSyncHeight, findNearProof, buildIndexerTxQuery, findFinalizationTxOnEthereum } from '@near-eth/utils' +import { + borshifyOutcomeProof, + urlParams, + nearOnEthSyncHeight, + findNearProof, + buildIndexerTxQuery, + findFinalizationTxOnEthereum, + parseNEARLockReceipt +} from '@near-eth/utils' import { getEthProvider, getNearWallet, getNearAccountId, getNearProvider, formatLargeNum, getSignerProvider, getBridgeParams } from '@near-eth/client/dist/utils' import { findReplacementTx, TxValidationError } from 'find-replacement-tx' @@ -283,7 +287,7 @@ export async function recover ( throw new Error(`Lock transaction failed: ${lockTxHash}`) } - const lockReceipt = await parseLockReceipt( + const lockReceipt = await parseNEARLockReceipt( lockTx, options.nativeNEARLockerAddress ?? bridgeParams.nativeNEARLockerAddress, nearProvider @@ -321,54 +325,6 @@ export async function recover ( return await checkSync(transfer, options) } -/** - * Parse the lock receipt id and block height needed to complete - * the step LOCK - * @param lockTx - * @param nativeNEARLockerAddress - * @param nearProvider - */ -export async function parseLockReceipt ( - lockTx: FinalExecutionOutcome, - nativeNEARLockerAddress: string, - nearProvider: najProviders.Provider -): Promise<{id: string, blockHeight: number, blockTimestamp: number, event: { amount: string, recipient: string }}> { - // @ts-expect-error - const bridgeReceipt: any = lockTx.receipts_outcome.find(r => r.outcome.executor_id === nativeNEARLockerAddress) - if (!bridgeReceipt) { - throw new Error(`Failed to parse bridge receipt for ${JSON.stringify(lockTx)}`) - } - const successValue = bridgeReceipt.outcome.status.SuccessValue - // eslint-disable-next-line @typescript-eslint/no-extraneous-class - class LockEvent { - constructor (args: any) { - Object.assign(this, args) - } - } - const SCHEMA = new Map([ - [LockEvent, { - kind: 'struct', - fields: [ - ['flag', 'u8'], - ['amount', 'u128'], - ['recipient', [20]] - ] - }] - ]) - const rawEvent = deserializeBorsh( - SCHEMA, LockEvent, Buffer.from(successValue, 'base64') - ) as { amount: BN, recipient: Uint8Array } - const event = { - amount: rawEvent.amount.toString(), - recipient: '0x' + Buffer.from(rawEvent.recipient).toString('hex') - } - - const receiptBlock = await nearProvider.block({ blockId: bridgeReceipt.block_hash }) - const blockHeight = Number(receiptBlock.header.height) - const blockTimestamp = Number(receiptBlock.header.timestamp) - return { id: bridgeReceipt.id, blockHeight, blockTimestamp, event } -} - /** * Initiate a transfer from NEAR to Ethereum by locking $NEAR tokens. * @param params Uses Named Arguments pattern, please pass arguments as object @@ -616,7 +572,7 @@ export async function checkLock ( let lockReceipt try { - lockReceipt = await parseLockReceipt( + lockReceipt = await parseNEARLockReceipt( lockTx, options.nativeNEARLockerAddress ?? bridgeParams.nativeNEARLockerAddress, nearProvider diff --git a/packages/nep141-erc20/src/bridged-nep141/sendToEthereum/index.ts b/packages/nep141-erc20/src/bridged-nep141/sendToEthereum/index.ts index c700c3cc..bc4f5cfa 100644 --- a/packages/nep141-erc20/src/bridged-nep141/sendToEthereum/index.ts +++ b/packages/nep141-erc20/src/bridged-nep141/sendToEthereum/index.ts @@ -2,15 +2,19 @@ import BN from 'bn.js' import bs58 from 'bs58' import { ethers } from 'ethers' import { Account, utils, providers as najProviders } from 'near-api-js' -import { FinalExecutionOutcome } from 'near-api-js/lib/providers' -import { - deserialize as deserializeBorsh -} from 'near-api-js/lib/utils/serialize' import * as status from '@near-eth/client/dist/statuses' import { stepsFor } from '@near-eth/client/dist/i18nHelpers' import { TransferStatus, TransactionInfo } from '@near-eth/client/dist/types' import { track, untrack } from '@near-eth/client' -import { borshifyOutcomeProof, urlParams, nearOnEthSyncHeight, findNearProof, buildIndexerTxQuery, findFinalizationTxOnEthereum } from '@near-eth/utils' +import { + borshifyOutcomeProof, + urlParams, + nearOnEthSyncHeight, + findNearProof, + buildIndexerTxQuery, + findFinalizationTxOnEthereum, + parseNep141BurnReceipt +} from '@near-eth/utils' import { findReplacementTx, TxValidationError } from 'find-replacement-tx' import { getEthProvider, getNearWallet, getNearAccountId, getNearProvider, formatLargeNum, getSignerProvider, getBridgeParams } from '@near-eth/client/dist/utils' import getNep141Address from '../getAddress' @@ -297,7 +301,7 @@ export async function recover ( } const nep141Factory = options.nep141Factory ?? getBridgeParams().nep141Factory - const withdrawReceipt = await parseWithdrawReceipt(withdrawTx, nep141Factory, nearProvider) + const withdrawReceipt = await parseNep141BurnReceipt(withdrawTx, nep141Factory, nearProvider) const { amount, recipient, token: erc20Address } = withdrawReceipt.event const symbol = options.symbol ?? await getSymbol({ erc20Address, options }) @@ -331,56 +335,6 @@ export async function recover ( return await checkSync(transfer, options) } -/** - * Parse the withdraw receipt id and block height needed to complete - * the step WITHDRAW - * @param withdrawTx - * @param nep141Factory - * @param nearProvider - */ -export async function parseWithdrawReceipt ( - withdrawTx: FinalExecutionOutcome, - nep141Factory: string, - nearProvider: najProviders.Provider -): Promise<{id: string, blockHeight: number, blockTimestamp: number, event: { amount: string, token: string, recipient: string }}> { - // @ts-expect-error - const bridgeReceipt: any = withdrawTx.receipts_outcome.find(r => r.outcome.executor_id === nep141Factory) - if (!bridgeReceipt) { - throw new Error(`Failed to parse bridge receipt for ${JSON.stringify(withdrawTx)}`) - } - const successValue = bridgeReceipt.outcome.status.SuccessValue - // eslint-disable-next-line @typescript-eslint/no-extraneous-class - class WithdrawEvent { - constructor (args: any) { - Object.assign(this, args) - } - } - const SCHEMA = new Map([ - [WithdrawEvent, { - kind: 'struct', - fields: [ - ['flag', 'u8'], - ['amount', 'u128'], - ['token', [20]], - ['recipient', [20]] - ] - }] - ]) - const rawEvent = deserializeBorsh( - SCHEMA, WithdrawEvent, Buffer.from(successValue, 'base64') - ) as { amount: BN, token: Uint8Array, recipient: Uint8Array} - const event = { - amount: rawEvent.amount.toString(), - token: '0x' + Buffer.from(rawEvent.token).toString('hex'), - recipient: '0x' + Buffer.from(rawEvent.recipient).toString('hex') - } - - const receiptBlock = await nearProvider.block({ blockId: bridgeReceipt.block_hash }) - const blockHeight = Number(receiptBlock.header.height) - const blockTimestamp = Number(receiptBlock.header.timestamp) - return { id: bridgeReceipt.id, blockHeight, blockTimestamp, event } -} - /** * Initiate a transfer from NEAR to Ethereum by burning minted tokens. * @param params Uses Named Arguments pattern, please pass arguments as object @@ -638,7 +592,7 @@ export async function checkWithdraw ( let withdrawReceipt const nep141Factory = options.nep141Factory ?? getBridgeParams().nep141Factory try { - withdrawReceipt = await parseWithdrawReceipt(withdrawTx, nep141Factory, nearProvider) + withdrawReceipt = await parseNep141BurnReceipt(withdrawTx, nep141Factory, nearProvider) } catch (e) { if (e instanceof TransferError) { if (clearParams) urlParams.clear(...clearParams) diff --git a/packages/utils/package.json b/packages/utils/package.json index 58926e52..d4e604b9 100644 --- a/packages/utils/package.json +++ b/packages/utils/package.json @@ -10,10 +10,12 @@ "build": "rm -rf dist && tsc --build tsconfig.json" }, "devDependencies": { + "@types/bn.js": "^5", "@types/bs58": "^4.0.1", "@types/node": "^14.14.28" }, "dependencies": { + "bn.js": "^5.2.1", "bs58": "^4.0.1", "eth-object": "aurora-is-near/eth-object#378b8dbf44a71f7049666cea5a16ab88d45aed06", "ethereumjs-util": "^7.0.10", diff --git a/packages/utils/src/findProof.ts b/packages/utils/src/findProof.ts index f393ffdb..a780e424 100644 --- a/packages/utils/src/findProof.ts +++ b/packages/utils/src/findProof.ts @@ -4,11 +4,16 @@ import { Trie } from 'lite-merkle-patricia-tree' // @ts-expect-error import { Header, Proof, Receipt, Log } from 'eth-object' import { rlp, toBuffer } from 'ethereumjs-util' -import { serialize as serializeBorsh } from 'near-api-js/lib/utils/serialize' +import { + deserialize as deserializeBorsh, + serialize as serializeBorsh +} from 'near-api-js/lib/utils/serialize' import { IdType } from 'near-api-js/lib/providers/provider' +import { FinalExecutionOutcome } from 'near-api-js/lib/providers' import { providers as najProviders } from 'near-api-js' import { ethers } from 'ethers' import bs58 from 'bs58' +import BN from 'bn.js' // eslint-disable-next-line @typescript-eslint/no-extraneous-class class BorshProof { @@ -185,3 +190,180 @@ export async function findNearProof ( }) return proof } + +/** + * Parse the burn receipt id and block height needed to build a proof. + * @param burnTx + * @param auroraEvmAccount + * @param nearProvider + */ +export async function parseETHBurnReceipt ( + burnTx: FinalExecutionOutcome, + auroraEvmAccount: string, + nearProvider: najProviders.Provider +): Promise<{id: string, blockHeight: number, blockTimestamp: number, event: { amount: string, recipient: string, etherCustodian: string }}> { + let event: any + let bridgeReceipt: any + burnTx.receipts_outcome.some((receipt) => { + // @ts-expect-error + if (receipt.outcome.executor_id !== auroraEvmAccount) return false + try { + // @ts-expect-error + const successValue = receipt.outcome.status.SuccessValue + // eslint-disable-next-line @typescript-eslint/no-extraneous-class + class WithdrawEvent { + constructor (args: any) { + Object.assign(this, args) + } + } + const SCHEMA = new Map([ + [WithdrawEvent, { + kind: 'struct', + fields: [ + ['amount', 'u128'], + ['recipient_id', [20]], + ['eth_custodian_address', [20]] + ] + }] + ]) + const rawEvent = deserializeBorsh( + SCHEMA, WithdrawEvent, Buffer.from(successValue, 'base64') + ) as { amount: BN, recipient_id: Uint8Array, eth_custodian_address: Uint8Array} + event = { + amount: rawEvent.amount.toString(), + recipient: '0x' + Buffer.from(rawEvent.recipient_id).toString('hex'), + etherCustodian: '0x' + Buffer.from(rawEvent.eth_custodian_address).toString('hex') + } + bridgeReceipt = receipt + return true + } catch (error) { + console.log(error) + } + return false + }) + if (!bridgeReceipt || !event) { + throw new Error(`Failed to parse bridge receipt for ${JSON.stringify(burnTx)}`) + } + const receiptBlock = await nearProvider.block({ blockId: bridgeReceipt.block_hash }) + const blockHeight = Number(receiptBlock.header.height) + const blockTimestamp = Number(receiptBlock.header.timestamp) + return { id: bridgeReceipt.id, blockHeight, blockTimestamp, event } +} + +/** + * Parse the lock receipt id and block height needed to build a proof. + * @param lockTx + * @param nativeNEARLockerAddress + * @param nearProvider + */ +export async function parseNEARLockReceipt ( + lockTx: FinalExecutionOutcome, + nativeNEARLockerAddress: string, + nearProvider: najProviders.Provider +): Promise<{id: string, blockHeight: number, blockTimestamp: number, event: { amount: string, recipient: string }}> { + let event: any + let bridgeReceipt: any + lockTx.receipts_outcome.some((receipt) => { + // @ts-expect-error + if (receipt.outcome.executor_id !== nativeNEARLockerAddress) return false + try { + // @ts-expect-error + const successValue = receipt.outcome.status.SuccessValue + // eslint-disable-next-line @typescript-eslint/no-extraneous-class + class LockEvent { + constructor (args: any) { + Object.assign(this, args) + } + } + const SCHEMA = new Map([ + [LockEvent, { + kind: 'struct', + fields: [ + ['flag', 'u8'], + ['amount', 'u128'], + ['recipient', [20]] + ] + }] + ]) + const rawEvent = deserializeBorsh( + SCHEMA, LockEvent, Buffer.from(successValue, 'base64') + ) as { amount: BN, recipient: Uint8Array } + event = { + amount: rawEvent.amount.toString(), + recipient: '0x' + Buffer.from(rawEvent.recipient).toString('hex') + } + bridgeReceipt = receipt + return true + } catch (error) { + console.log(error) + } + return false + }) + if (!bridgeReceipt || !event) { + throw new Error(`Failed to parse bridge receipt for ${JSON.stringify(lockTx)}`) + } + const receiptBlock = await nearProvider.block({ blockId: bridgeReceipt.block_hash }) + const blockHeight = Number(receiptBlock.header.height) + const blockTimestamp = Number(receiptBlock.header.timestamp) + return { id: bridgeReceipt.id, blockHeight, blockTimestamp, event } +} + +/** + * Parse the burn receipt id and block height needed to build a proof. + * @param burnTx + * @param nep141Factory + * @param nearProvider + */ +export async function parseNep141BurnReceipt ( + burnTx: FinalExecutionOutcome, + nep141Factory: string, + nearProvider: najProviders.Provider +): Promise<{id: string, blockHeight: number, blockTimestamp: number, event: { amount: string, token: string, recipient: string }}> { + let event: any + let bridgeReceipt: any + burnTx.receipts_outcome.some((receipt) => { + // @ts-expect-error + if (receipt.outcome.executor_id !== nep141Factory) return false + try { + // @ts-expect-error + const successValue = receipt.outcome.status.SuccessValue + // eslint-disable-next-line @typescript-eslint/no-extraneous-class + class WithdrawEvent { + constructor (args: any) { + Object.assign(this, args) + } + } + const SCHEMA = new Map([ + [WithdrawEvent, { + kind: 'struct', + fields: [ + ['flag', 'u8'], + ['amount', 'u128'], + ['token', [20]], + ['recipient', [20]] + ] + }] + ]) + const rawEvent = deserializeBorsh( + SCHEMA, WithdrawEvent, Buffer.from(successValue, 'base64') + ) as { amount: BN, token: Uint8Array, recipient: Uint8Array} + event = { + amount: rawEvent.amount.toString(), + token: '0x' + Buffer.from(rawEvent.token).toString('hex'), + recipient: '0x' + Buffer.from(rawEvent.recipient).toString('hex') + } + bridgeReceipt = receipt + return true + } catch (error) { + console.log(error) + } + return false + }) + if (!bridgeReceipt || !event) { + throw new Error(`Failed to parse bridge receipt for ${JSON.stringify(burnTx)}`) + } + const receiptBlock = await nearProvider.block({ blockId: bridgeReceipt.block_hash }) + const blockHeight = Number(receiptBlock.header.height) + const blockTimestamp = Number(receiptBlock.header.timestamp) + return { id: bridgeReceipt.id, blockHeight, blockTimestamp, event } +} diff --git a/packages/utils/src/index.ts b/packages/utils/src/index.ts index 6efd3dbc..9d161c3c 100644 --- a/packages/utils/src/index.ts +++ b/packages/utils/src/index.ts @@ -6,5 +6,11 @@ export { findFinalizationTxOnEthereum, findFinalizationTxOnNear, ExplorerIndexer export { borshifyOutcomeProof } from './borshify-proof' export { ethOnNearSyncHeight } from './ethOnNearClient' export { nearOnEthSyncHeight } from './nearOnEthClient' -export { findEthProof, findNearProof } from './findProof' +export { + findEthProof, + findNearProof, + parseETHBurnReceipt, + parseNEARLockReceipt, + parseNep141BurnReceipt +} from './findProof' export { buildIndexerTxQuery } from './indexer' diff --git a/yarn.lock b/yarn.lock index df583d1a..ef44d793 100644 --- a/yarn.lock +++ b/yarn.lock @@ -831,8 +831,10 @@ __metadata: version: 0.0.0-use.local resolution: "@near-eth/utils@workspace:packages/utils" dependencies: + "@types/bn.js": "npm:^5" "@types/bs58": "npm:^4.0.1" "@types/node": "npm:^14.14.28" + bn.js: "npm:^5.2.1" bs58: "npm:^4.0.1" eth-object: "aurora-is-near/eth-object#378b8dbf44a71f7049666cea5a16ab88d45aed06" ethereumjs-util: "npm:^7.0.10" @@ -938,6 +940,15 @@ __metadata: languageName: node linkType: hard +"@types/bn.js@npm:^5": + version: 5.1.5 + resolution: "@types/bn.js@npm:5.1.5" + dependencies: + "@types/node": "npm:*" + checksum: 9719330c86aeae0a6a447c974cf0f853ba3660ede20de61f435b03d699e30e6d8b35bf71a8dc9fdc8317784438e83177644ba068ed653d0ae0106e1ecbfe289e + languageName: node + linkType: hard + "@types/bn.js@npm:^5.1.0": version: 5.1.0 resolution: "@types/bn.js@npm:5.1.0"