diff --git a/frontend/src/consts/index.ts b/frontend/src/consts/index.ts index 2afa7d79..98ab6fbe 100644 --- a/frontend/src/consts/index.ts +++ b/frontend/src/consts/index.ts @@ -1,7 +1,7 @@ import { FUNGIBLE_TOKEN_ABI } from './abi'; import { VARA_NODE_ADDRESS, ETH_NODE_ADDRESS, ETH_CHAIN_ID, BRIDGING_PAYMENT_CONTRACT_ADDRESS } from './env'; import { ROUTE } from './routing'; -import { BridgingPaymentProgram, VftGatewayProgram, VftProgram } from './sails'; +import { BridgingPaymentProgram, VftManagerProgram, VftProgram } from './sails'; export { FUNGIBLE_TOKEN_ABI, @@ -11,6 +11,6 @@ export { BRIDGING_PAYMENT_CONTRACT_ADDRESS, ROUTE, BridgingPaymentProgram, - VftGatewayProgram, + VftManagerProgram, VftProgram, }; diff --git a/frontend/src/consts/sails/bridging-payment.ts b/frontend/src/consts/sails/bridging-payment.ts index a48f2ce8..3ba3eeef 100644 --- a/frontend/src/consts/sails/bridging-payment.ts +++ b/frontend/src/consts/sails/bridging-payment.ts @@ -1,107 +1,36 @@ /* eslint-disable @typescript-eslint/no-floating-promises */ /* eslint-disable @typescript-eslint/no-explicit-any */ - import { GearApi, decodeAddress } from '@gear-js/api'; import { TypeRegistry } from '@polkadot/types'; -import { - ActorId, - H160, - TransactionBuilder, - MessageId, - getServiceNamePrefix, - getFnNamePrefix, - ZERO_ADDRESS, -} from 'sails-js'; +import { TransactionBuilder, H160, ActorId, getServiceNamePrefix, getFnNamePrefix, ZERO_ADDRESS } from 'sails-js'; export interface InitConfig { admin_address: ActorId; - vft_gateway_address: ActorId; + vft_manager_address: ActorId; config: Config; } export interface Config { fee: number | string | bigint; gas_for_reply_deposit: number | string | bigint; - gas_to_send_request_to_gateway: number | string | bigint; - gas_to_transfer_tokens: number | string | bigint; + gas_to_send_request_to_vft_manager: number | string | bigint; reply_timeout: number; - gas_for_request_to_gateway_msg: number | string | bigint; -} - -export interface MessageInfo { - status: MessageStatus; - details: TransactionDetails; + gas_for_request_to_vft_manager_msg: number | string | bigint; } -export type MessageStatus = - | { sendingMessageToTransferTokens: null } - | { tokenTransferCompleted: boolean } - | { waitingReplyFromTokenTransfer: null } - | { sendingMessageToGateway: null } - | { gatewayMessageProcessingCompleted: [number | string | bigint, H160] } - | { waitingReplyFromGateway: null } - | { messageToGatewayStep: null } - | { returnTokensBackStep: null } - | { sendingMessageToTransferTokensBack: null } - | { waitingReplyFromTokenTransferBack: null } - | { tokenTransferBackCompleted: null } - | { messageProcessedWithSuccess: [number | string | bigint, H160] }; - -export type TransactionDetails = - | { transfer: { sender: ActorId; receiver: ActorId; amount: number | string | bigint; token_id: ActorId } } - | { - sendMessageToGateway: { - sender: ActorId; - vara_token_id: ActorId; - amount: number | string | bigint; - receiver: H160; - attached_value: number | string | bigint; - }; - }; - export class Program { public readonly registry: TypeRegistry; public readonly bridgingPayment: BridgingPayment; constructor(public api: GearApi, public programId?: `0x${string}`) { const types: Record = { - InitConfig: { admin_address: '[u8;32]', vft_gateway_address: '[u8;32]', config: 'Config' }, + InitConfig: { admin_address: '[u8;32]', vft_manager_address: '[u8;32]', config: 'Config' }, Config: { fee: 'u128', gas_for_reply_deposit: 'u64', - gas_to_send_request_to_gateway: 'u64', - gas_to_transfer_tokens: 'u64', + gas_to_send_request_to_vft_manager: 'u64', reply_timeout: 'u32', - gas_for_request_to_gateway_msg: 'u64', - }, - MessageInfo: { status: 'MessageStatus', details: 'TransactionDetails' }, - MessageStatus: { - _enum: { - SendingMessageToTransferTokens: 'Null', - TokenTransferCompleted: 'bool', - WaitingReplyFromTokenTransfer: 'Null', - SendingMessageToGateway: 'Null', - GatewayMessageProcessingCompleted: '(U256, H160)', - WaitingReplyFromGateway: 'Null', - MessageToGatewayStep: 'Null', - ReturnTokensBackStep: 'Null', - SendingMessageToTransferTokensBack: 'Null', - WaitingReplyFromTokenTransferBack: 'Null', - TokenTransferBackCompleted: 'Null', - MessageProcessedWithSuccess: '(U256, H160)', - }, - }, - TransactionDetails: { - _enum: { - Transfer: { sender: '[u8;32]', receiver: '[u8;32]', amount: 'U256', token_id: '[u8;32]' }, - SendMessageToGateway: { - sender: '[u8;32]', - vara_token_id: '[u8;32]', - amount: 'U256', - receiver: 'H160', - attached_value: 'u128', - }, - }, + gas_for_request_to_vft_manager_msg: 'u64', }, }; @@ -146,14 +75,18 @@ export class Program { export class BridgingPayment { constructor(private _program: Program) {} - public continueTransaction(msg_id: MessageId): TransactionBuilder { + public makeRequest( + amount: number | string | bigint, + receiver: H160, + vara_token_id: ActorId, + ): TransactionBuilder { if (!this._program.programId) throw new Error('Program ID is not set'); return new TransactionBuilder( this._program.api, this._program.registry, 'send_message', - ['BridgingPayment', 'ContinueTransaction', msg_id], - '(String, String, [u8;32])', + ['BridgingPayment', 'MakeRequest', amount, receiver, vara_token_id], + '(String, String, U256, H160, [u8;32])', 'Null', this._program.programId, ); @@ -172,31 +105,14 @@ export class BridgingPayment { ); } - public requestToGateway( - amount: number | string | bigint, - receiver: H160, - vara_token_id: ActorId, - ): TransactionBuilder { - if (!this._program.programId) throw new Error('Program ID is not set'); - return new TransactionBuilder( - this._program.api, - this._program.registry, - 'send_message', - ['BridgingPayment', 'RequestToGateway', amount, receiver, vara_token_id], - '(String, String, U256, H160, [u8;32])', - 'Null', - this._program.programId, - ); - } - - public returnTokens(msg_id: MessageId): TransactionBuilder { + public setConfig(config: Config): TransactionBuilder { if (!this._program.programId) throw new Error('Program ID is not set'); return new TransactionBuilder( this._program.api, this._program.registry, 'send_message', - ['BridgingPayment', 'ReturnTokens', msg_id], - '(String, String, [u8;32])', + ['BridgingPayment', 'SetConfig', config], + '(String, String, Config)', 'Null', this._program.programId, ); @@ -215,42 +131,13 @@ export class BridgingPayment { ); } - public updateConfig( - fee: number | string | bigint | null, - gas_for_reply_deposit: number | string | bigint | null, - gas_to_send_request_to_gateway: number | string | bigint | null, - gas_to_transfer_tokens: number | string | bigint | null, - reply_timeout: number | null, - gas_for_request_to_gateway_msg: number | string | bigint | null, - ): TransactionBuilder { + public updateVftManagerAddress(new_vft_manager_address: ActorId): TransactionBuilder { if (!this._program.programId) throw new Error('Program ID is not set'); return new TransactionBuilder( this._program.api, this._program.registry, 'send_message', - [ - 'BridgingPayment', - 'UpdateConfig', - fee, - gas_for_reply_deposit, - gas_to_send_request_to_gateway, - gas_to_transfer_tokens, - reply_timeout, - gas_for_request_to_gateway_msg, - ], - '(String, String, Option, Option, Option, Option, Option, Option)', - 'Null', - this._program.programId, - ); - } - - public updateVftGatewayAddress(new_vft_gateway_address: ActorId): TransactionBuilder { - if (!this._program.programId) throw new Error('Program ID is not set'); - return new TransactionBuilder( - this._program.api, - this._program.registry, - 'send_message', - ['BridgingPayment', 'UpdateVftGatewayAddress', new_vft_gateway_address], + ['BridgingPayment', 'UpdateVftManagerAddress', new_vft_manager_address], '(String, String, [u8;32])', 'Null', this._program.programId, @@ -295,34 +182,13 @@ export class BridgingPayment { return result[2].toJSON() as unknown as Config; } - public async msgTrackerState( - originAddress?: string, - value?: number | string | bigint, - atBlock?: `0x${string}`, - ): Promise> { - const payload = this._program.registry - .createType('(String, String)', ['BridgingPayment', 'MsgTrackerState']) - .toHex(); - const reply = await this._program.api.message.calculateReply({ - destination: this._program.programId!, - origin: originAddress ? decodeAddress(originAddress) : ZERO_ADDRESS, - payload, - value: value || 0, - gasLimit: this._program.api.blockGasLimit.toBigInt(), - at: atBlock, - }); - if (!reply.code.isSuccess) throw new Error(this._program.registry.createType('String', reply.payload).toString()); - const result = this._program.registry.createType('(String, String, Vec<([u8;32], MessageInfo)>)', reply.payload); - return result[2].toJSON() as unknown as Array<[MessageId, MessageInfo]>; - } - - public async vftGatewayAddress( + public async vftManagerAddress( originAddress?: string, value?: number | string | bigint, atBlock?: `0x${string}`, ): Promise { const payload = this._program.registry - .createType('(String, String)', ['BridgingPayment', 'VftGatewayAddress']) + .createType('(String, String)', ['BridgingPayment', 'VftManagerAddress']) .toHex(); const reply = await this._program.api.message.calculateReply({ destination: this._program.programId!, diff --git a/frontend/src/consts/sails/index.ts b/frontend/src/consts/sails/index.ts index 678d4be7..15cefabd 100644 --- a/frontend/src/consts/sails/index.ts +++ b/frontend/src/consts/sails/index.ts @@ -1,5 +1,5 @@ import { Program as BridgingPaymentProgram } from './bridging-payment'; import { Program as VftProgram } from './extended_vft'; -import { Program as VftGatewayProgram } from './vft-gateway'; +import { Program as VftManagerProgram } from './vft-manager'; -export { BridgingPaymentProgram, VftGatewayProgram, VftProgram }; +export { BridgingPaymentProgram, VftManagerProgram, VftProgram }; diff --git a/frontend/src/consts/sails/vft-gateway.ts b/frontend/src/consts/sails/vft-manager.ts similarity index 52% rename from frontend/src/consts/sails/vft-gateway.ts rename to frontend/src/consts/sails/vft-manager.ts index 3b58cbaf..92c05389 100644 --- a/frontend/src/consts/sails/vft-gateway.ts +++ b/frontend/src/consts/sails/vft-manager.ts @@ -1,135 +1,169 @@ /* eslint-disable @typescript-eslint/no-floating-promises */ /* eslint-disable @typescript-eslint/no-explicit-any */ - import { GearApi, decodeAddress } from '@gear-js/api'; import { TypeRegistry } from '@polkadot/types'; -import { H160, ActorId, TransactionBuilder, MessageId, ZERO_ADDRESS } from 'sails-js'; +import { + TransactionBuilder, + H160, + MessageId, + ActorId, + getServiceNamePrefix, + getFnNamePrefix, + ZERO_ADDRESS, +} from 'sails-js'; export interface InitConfig { - receiver_contract_address: H160; + erc20_manager_address: H160; gear_bridge_builtin: ActorId; + eth_client: ActorId; config: Config; } export interface Config { - gas_to_burn_tokens: number | string | bigint; + gas_for_token_ops: number | string | bigint; gas_for_reply_deposit: number | string | bigint; - gas_to_mint_tokens: number | string | bigint; + gas_for_submit_receipt: number | string | bigint; gas_to_send_request_to_builtin: number | string | bigint; reply_timeout: number; - gas_for_transfer_to_eth_msg: number | string | bigint; + gas_for_request_bridging: number | string | bigint; } export type Error = - | 'sendError' - | 'replyError' - | 'burnTokensDecodeError' - | 'errorDuringTokensBurn' - | 'requestToBuiltinSendError' - | 'requestToBuiltinReplyError' - | 'builtinDecodeError' - | 'payloadSizeError' - | 'mintTokensDecodeError' - | 'replyTimeoutError' - | 'errorDuringTokensMint' + | 'sendFailure' + | 'replyFailure' + | 'burnTokensDecode' + | 'transferFromDecode' + | 'builtinDecode' + | 'mintTokensDecode' + | 'replyTimeout' | 'noCorrespondingEthAddress' | 'replyHook' | 'messageNotFound' | 'invalidMessageStatus' | 'messageFailed' | 'burnTokensFailed' + | 'lockTokensFailed' | 'bridgeBuiltinMessageFailed' - | 'tokensRefundedError'; + | 'tokensRefunded' + | 'notEthClient' + | 'notEnoughGas' + | 'noCorrespondingVaraAddress' + | 'notSupportedEvent'; + +export type TokenSupply = 'ethereum' | 'gear'; export interface MessageInfo { status: MessageStatus; - details: TransactionDetails; + details: TxDetails; } export type MessageStatus = - | { sendingMessageToBurnTokens: null } - | { tokenBurnCompleted: boolean } - | { waitingReplyFromBurn: null } | { sendingMessageToBridgeBuiltin: null } | { bridgeResponseReceived: number | string | bigint | null } | { waitingReplyFromBuiltin: null } | { bridgeBuiltinStep: null } + | { sendingMessageToBurnTokens: null } + | { tokenBurnCompleted: boolean } + | { waitingReplyFromBurn: null } | { sendingMessageToMintTokens: null } | { tokenMintCompleted: null } | { waitingReplyFromMint: null } | { mintTokensStep: null } + | { sendingMessageToLockTokens: null } + | { tokenLockCompleted: boolean } + | { waitingReplyFromLock: null } + | { sendingMessageToUnlockTokens: null } + | { tokenUnlockCompleted: null } + | { waitingReplyFromUnlock: null } + | { unlockTokensStep: null } | { messageProcessedWithSuccess: number | string | bigint }; -export interface TransactionDetails { - vara_token_id: ActorId; - sender: ActorId; - amount: number | string | bigint; - receiver: H160; -} +export type TxDetails = + | { requestBridging: { vara_token_id: ActorId; sender: ActorId; amount: number | string | bigint; receiver: H160 } } + | { submitReceipt: { vara_token_id: ActorId; receiver: ActorId; amount: number | string | bigint } }; export class Program { public readonly registry: TypeRegistry; - public readonly vftGateway: VftGateway; + public readonly vftManager: VftManager; constructor(public api: GearApi, public programId?: `0x${string}`) { const types: Record = { - InitConfig: { receiver_contract_address: 'H160', gear_bridge_builtin: '[u8;32]', config: 'Config' }, + InitConfig: { + erc20_manager_address: 'H160', + gear_bridge_builtin: '[u8;32]', + eth_client: '[u8;32]', + config: 'Config', + }, Config: { - gas_to_burn_tokens: 'u64', + gas_for_token_ops: 'u64', gas_for_reply_deposit: 'u64', - gas_to_mint_tokens: 'u64', + gas_for_submit_receipt: 'u64', gas_to_send_request_to_builtin: 'u64', reply_timeout: 'u32', - gas_for_transfer_to_eth_msg: 'u64', + gas_for_request_bridging: 'u64', }, Error: { _enum: [ - 'SendError', - 'ReplyError', - 'BurnTokensDecodeError', - 'ErrorDuringTokensBurn', - 'RequestToBuiltinSendError', - 'RequestToBuiltinReplyError', - 'BuiltinDecodeError', - 'PayloadSizeError', - 'MintTokensDecodeError', - 'ReplyTimeoutError', - 'ErrorDuringTokensMint', + 'SendFailure', + 'ReplyFailure', + 'BurnTokensDecode', + 'TransferFromDecode', + 'BuiltinDecode', + 'MintTokensDecode', + 'ReplyTimeout', 'NoCorrespondingEthAddress', 'ReplyHook', 'MessageNotFound', 'InvalidMessageStatus', 'MessageFailed', 'BurnTokensFailed', + 'LockTokensFailed', 'BridgeBuiltinMessageFailed', - 'TokensRefundedError', + 'TokensRefunded', + 'NotEthClient', + 'NotEnoughGas', + 'NoCorrespondingVaraAddress', + 'NotSupportedEvent', ], }, - MessageInfo: { status: 'MessageStatus', details: 'TransactionDetails' }, + TokenSupply: { _enum: ['Ethereum', 'Gear'] }, + MessageInfo: { status: 'MessageStatus', details: 'TxDetails' }, MessageStatus: { _enum: { - SendingMessageToBurnTokens: 'Null', - TokenBurnCompleted: 'bool', - WaitingReplyFromBurn: 'Null', SendingMessageToBridgeBuiltin: 'Null', BridgeResponseReceived: 'Option', WaitingReplyFromBuiltin: 'Null', BridgeBuiltinStep: 'Null', + SendingMessageToBurnTokens: 'Null', + TokenBurnCompleted: 'bool', + WaitingReplyFromBurn: 'Null', SendingMessageToMintTokens: 'Null', TokenMintCompleted: 'Null', WaitingReplyFromMint: 'Null', MintTokensStep: 'Null', + SendingMessageToLockTokens: 'Null', + TokenLockCompleted: 'bool', + WaitingReplyFromLock: 'Null', + SendingMessageToUnlockTokens: 'Null', + TokenUnlockCompleted: 'Null', + WaitingReplyFromUnlock: 'Null', + UnlockTokensStep: 'Null', MessageProcessedWithSuccess: 'U256', }, }, - TransactionDetails: { vara_token_id: '[u8;32]', sender: '[u8;32]', amount: 'U256', receiver: 'H160' }, + TxDetails: { + _enum: { + RequestBridging: { vara_token_id: '[u8;32]', sender: '[u8;32]', amount: 'U256', receiver: 'H160' }, + SubmitReceipt: { vara_token_id: '[u8;32]', receiver: '[u8;32]', amount: 'U256' }, + }, + }, }; this.registry = new TypeRegistry(); this.registry.setKnownTypes({ types }); this.registry.register(types); - this.vftGateway = new VftGateway(this); + this.vftManager = new VftManager(this); } newCtorFromCode(code: Uint8Array | Buffer, init_config: InitConfig): TransactionBuilder { @@ -163,7 +197,7 @@ export class Program { } } -export class VftGateway { +export class VftManager { constructor(private _program: Program) {} public handleInterruptedTransfer( @@ -174,21 +208,25 @@ export class VftGateway { this._program.api, this._program.registry, 'send_message', - ['VftGateway', 'HandleInterruptedTransfer', msg_id], + ['VftManager', 'HandleInterruptedTransfer', msg_id], '(String, String, [u8;32])', 'Result<(U256, H160), Error>', this._program.programId, ); } - public mapVaraToEthAddress(vara_token_id: ActorId, eth_token_id: H160): TransactionBuilder { + public mapVaraToEthAddress( + vara_token_id: ActorId, + eth_token_id: H160, + supply_type: TokenSupply, + ): TransactionBuilder { if (!this._program.programId) throw new Error('Program ID is not set'); return new TransactionBuilder( this._program.api, this._program.registry, 'send_message', - ['VftGateway', 'MapVaraToEthAddress', vara_token_id, eth_token_id], - '(String, String, [u8;32], H160)', + ['VftManager', 'MapVaraToEthAddress', vara_token_id, eth_token_id, supply_type], + '(String, String, [u8;32], H160, TokenSupply)', 'Null', this._program.programId, ); @@ -200,14 +238,20 @@ export class VftGateway { this._program.api, this._program.registry, 'send_message', - ['VftGateway', 'RemoveVaraToEthAddress', vara_token_id], + ['VftManager', 'RemoveVaraToEthAddress', vara_token_id], '(String, String, [u8;32])', 'Null', this._program.programId, ); } - public transferVaraToEth( + /** + * Request bridging of tokens from gear to ethereum. It involves locking/burning + * `vft` tokens (specific operation depends on the token supply type) and sending + * request to the bridge built-in actor. + */ + public requestBridging( + sender: ActorId, vara_token_id: ActorId, amount: number | string | bigint, receiver: H160, @@ -217,61 +261,76 @@ export class VftGateway { this._program.api, this._program.registry, 'send_message', - ['VftGateway', 'TransferVaraToEth', vara_token_id, amount, receiver], - '(String, String, [u8;32], U256, H160)', + ['VftManager', 'RequestBridging', sender, vara_token_id, amount, receiver], + '(String, String, [u8;32], [u8;32], U256, H160)', 'Result<(U256, H160), Error>', this._program.programId, ); } - public updateConfig( - gas_to_burn_tokens: number | string | bigint | null, - gas_to_mint_tokens: number | string | bigint | null, - gas_for_reply_deposit: number | string | bigint | null, - gas_to_send_request_to_builtin: number | string | bigint | null, - reply_timeout: number | null, - gas_for_transfer_to_eth_msg: number | string | bigint | null, - ): TransactionBuilder { + /** + * Submit rlp-encoded transaction receipt. This receipt is decoded under the hood + * and checked that it's a valid receipt from tx send to `ERC20Manager` contract. + * This entrypoint can be called only by `ethereum-event-client`. + */ + public submitReceipt(receipt_rlp: `0x${string}`): TransactionBuilder<{ ok: null } | { err: Error }> { + if (!this._program.programId) throw new Error('Program ID is not set'); + return new TransactionBuilder<{ ok: null } | { err: Error }>( + this._program.api, + this._program.registry, + 'send_message', + ['VftManager', 'SubmitReceipt', receipt_rlp], + '(String, String, Vec)', + 'Result', + this._program.programId, + ); + } + + public updateConfig(config: Config): TransactionBuilder { if (!this._program.programId) throw new Error('Program ID is not set'); return new TransactionBuilder( this._program.api, this._program.registry, 'send_message', - [ - 'VftGateway', - 'UpdateConfig', - gas_to_burn_tokens, - gas_to_mint_tokens, - gas_for_reply_deposit, - gas_to_send_request_to_builtin, - reply_timeout, - gas_for_transfer_to_eth_msg, - ], - '(String, String, Option, Option, Option, Option, Option, Option)', + ['VftManager', 'UpdateConfig', config], + '(String, String, Config)', 'Null', this._program.programId, ); } - public updateReceiverContractAddress(new_receiver_contract_address: H160): TransactionBuilder { + public updateErc20ManagerAddress(new_erc20_manager_address: H160): TransactionBuilder { if (!this._program.programId) throw new Error('Program ID is not set'); return new TransactionBuilder( this._program.api, this._program.registry, 'send_message', - ['VftGateway', 'UpdateReceiverContractAddress', new_receiver_contract_address], + ['VftManager', 'UpdateErc20ManagerAddress', new_erc20_manager_address], '(String, String, H160)', 'Null', this._program.programId, ); } + public updateEthClient(eth_client_new: ActorId): TransactionBuilder { + if (!this._program.programId) throw new Error('Program ID is not set'); + return new TransactionBuilder( + this._program.api, + this._program.registry, + 'send_message', + ['VftManager', 'UpdateEthClient', eth_client_new], + '(String, String, [u8;32])', + 'Null', + this._program.programId, + ); + } + public async admin( originAddress?: string, value?: number | string | bigint, atBlock?: `0x${string}`, ): Promise { - const payload = this._program.registry.createType('(String, String)', ['VftGateway', 'Admin']).toHex(); + const payload = this._program.registry.createType('(String, String)', ['VftManager', 'Admin']).toHex(); const reply = await this._program.api.message.calculateReply({ destination: this._program.programId!, origin: originAddress ? decodeAddress(originAddress) : ZERO_ADDRESS, @@ -285,12 +344,33 @@ export class VftGateway { return result[2].toJSON() as unknown as ActorId; } - public async gearBridgeBuiltin( + public async erc20ManagerAddress( + originAddress?: string, + value?: number | string | bigint, + atBlock?: `0x${string}`, + ): Promise { + const payload = this._program.registry + .createType('(String, String)', ['VftManager', 'Erc20ManagerAddress']) + .toHex(); + const reply = await this._program.api.message.calculateReply({ + destination: this._program.programId!, + origin: originAddress ? decodeAddress(originAddress) : ZERO_ADDRESS, + payload, + value: value || 0, + gasLimit: this._program.api.blockGasLimit.toBigInt(), + at: atBlock, + }); + if (!reply.code.isSuccess) throw new Error(this._program.registry.createType('String', reply.payload).toString()); + const result = this._program.registry.createType('(String, String, H160)', reply.payload); + return result[2].toJSON() as unknown as H160; + } + + public async ethClient( originAddress?: string, value?: number | string | bigint, atBlock?: `0x${string}`, ): Promise { - const payload = this._program.registry.createType('(String, String)', ['VftGateway', 'GearBridgeBuiltin']).toHex(); + const payload = this._program.registry.createType('(String, String)', ['VftManager', 'EthClient']).toHex(); const reply = await this._program.api.message.calculateReply({ destination: this._program.programId!, origin: originAddress ? decodeAddress(originAddress) : ZERO_ADDRESS, @@ -304,12 +384,12 @@ export class VftGateway { return result[2].toJSON() as unknown as ActorId; } - public async getConfig( + public async gearBridgeBuiltin( originAddress?: string, value?: number | string | bigint, atBlock?: `0x${string}`, - ): Promise { - const payload = this._program.registry.createType('(String, String)', ['VftGateway', 'GetConfig']).toHex(); + ): Promise { + const payload = this._program.registry.createType('(String, String)', ['VftManager', 'GearBridgeBuiltin']).toHex(); const reply = await this._program.api.message.calculateReply({ destination: this._program.programId!, origin: originAddress ? decodeAddress(originAddress) : ZERO_ADDRESS, @@ -319,16 +399,16 @@ export class VftGateway { at: atBlock, }); if (!reply.code.isSuccess) throw new Error(this._program.registry.createType('String', reply.payload).toString()); - const result = this._program.registry.createType('(String, String, Config)', reply.payload); - return result[2].toJSON() as unknown as Config; + const result = this._program.registry.createType('(String, String, [u8;32])', reply.payload); + return result[2].toJSON() as unknown as ActorId; } - public async msgTrackerState( + public async getConfig( originAddress?: string, value?: number | string | bigint, atBlock?: `0x${string}`, - ): Promise> { - const payload = this._program.registry.createType('(String, String)', ['VftGateway', 'MsgTrackerState']).toHex(); + ): Promise { + const payload = this._program.registry.createType('(String, String)', ['VftManager', 'GetConfig']).toHex(); const reply = await this._program.api.message.calculateReply({ destination: this._program.programId!, origin: originAddress ? decodeAddress(originAddress) : ZERO_ADDRESS, @@ -338,18 +418,16 @@ export class VftGateway { at: atBlock, }); if (!reply.code.isSuccess) throw new Error(this._program.registry.createType('String', reply.payload).toString()); - const result = this._program.registry.createType('(String, String, Vec<([u8;32], MessageInfo)>)', reply.payload); - return result[2].toJSON() as unknown as Array<[MessageId, MessageInfo]>; + const result = this._program.registry.createType('(String, String, Config)', reply.payload); + return result[2].toJSON() as unknown as Config; } - public async receiverContractAddress( + public async msgTrackerState( originAddress?: string, value?: number | string | bigint, atBlock?: `0x${string}`, - ): Promise { - const payload = this._program.registry - .createType('(String, String)', ['VftGateway', 'ReceiverContractAddress']) - .toHex(); + ): Promise> { + const payload = this._program.registry.createType('(String, String)', ['VftManager', 'MsgTrackerState']).toHex(); const reply = await this._program.api.message.calculateReply({ destination: this._program.programId!, origin: originAddress ? decodeAddress(originAddress) : ZERO_ADDRESS, @@ -359,16 +437,16 @@ export class VftGateway { at: atBlock, }); if (!reply.code.isSuccess) throw new Error(this._program.registry.createType('String', reply.payload).toString()); - const result = this._program.registry.createType('(String, String, H160)', reply.payload); - return result[2].toJSON() as unknown as H160; + const result = this._program.registry.createType('(String, String, Vec<([u8;32], MessageInfo)>)', reply.payload); + return result[2].toJSON() as unknown as Array<[MessageId, MessageInfo]>; } public async varaToEthAddresses( originAddress?: string, value?: number | string | bigint, atBlock?: `0x${string}`, - ): Promise> { - const payload = this._program.registry.createType('(String, String)', ['VftGateway', 'VaraToEthAddresses']).toHex(); + ): Promise> { + const payload = this._program.registry.createType('(String, String)', ['VftManager', 'VaraToEthAddresses']).toHex(); const reply = await this._program.api.message.calculateReply({ destination: this._program.programId!, origin: originAddress ? decodeAddress(originAddress) : ZERO_ADDRESS, @@ -378,7 +456,82 @@ export class VftGateway { at: atBlock, }); if (!reply.code.isSuccess) throw new Error(this._program.registry.createType('String', reply.payload).toString()); - const result = this._program.registry.createType('(String, String, Vec<([u8;32], H160)>)', reply.payload); - return result[2].toJSON() as unknown as Array<[ActorId, H160]>; + const result = this._program.registry.createType( + '(String, String, Vec<([u8;32], H160, TokenSupply)>)', + reply.payload, + ); + return result[2].toJSON() as unknown as Array<[ActorId, H160, TokenSupply]>; + } + + public subscribeToTokenMappingAddedEvent( + callback: (data: { vara_token_id: ActorId; eth_token_id: H160 }) => void | Promise, + ): Promise<() => void> { + return this._program.api.gearEvents.subscribeToGearEvent('UserMessageSent', ({ data: { message } }) => { + if (!message.source.eq(this._program.programId) || !message.destination.eq(ZERO_ADDRESS)) { + return; + } + + const payload = message.payload.toHex(); + if (getServiceNamePrefix(payload) === 'VftManager' && getFnNamePrefix(payload) === 'TokenMappingAdded') { + callback( + this._program.registry + .createType('(String, String, {"vara_token_id":"[u8;32]","eth_token_id":"H160"})', message.payload)[2] + .toJSON() as unknown as { vara_token_id: ActorId; eth_token_id: H160 }, + ); + } + }); + } + + public subscribeToTokenMappingRemovedEvent( + callback: (data: { vara_token_id: ActorId; eth_token_id: H160 }) => void | Promise, + ): Promise<() => void> { + return this._program.api.gearEvents.subscribeToGearEvent('UserMessageSent', ({ data: { message } }) => { + if (!message.source.eq(this._program.programId) || !message.destination.eq(ZERO_ADDRESS)) { + return; + } + + const payload = message.payload.toHex(); + if (getServiceNamePrefix(payload) === 'VftManager' && getFnNamePrefix(payload) === 'TokenMappingRemoved') { + callback( + this._program.registry + .createType('(String, String, {"vara_token_id":"[u8;32]","eth_token_id":"H160"})', message.payload)[2] + .toJSON() as unknown as { vara_token_id: ActorId; eth_token_id: H160 }, + ); + } + }); + } + + public subscribeToBridgingRequestedEvent( + callback: (data: { + nonce: number | string | bigint; + vara_token_id: ActorId; + amount: number | string | bigint; + sender: ActorId; + receiver: H160; + }) => void | Promise, + ): Promise<() => void> { + return this._program.api.gearEvents.subscribeToGearEvent('UserMessageSent', ({ data: { message } }) => { + if (!message.source.eq(this._program.programId) || !message.destination.eq(ZERO_ADDRESS)) { + return; + } + + const payload = message.payload.toHex(); + if (getServiceNamePrefix(payload) === 'VftManager' && getFnNamePrefix(payload) === 'BridgingRequested') { + callback( + this._program.registry + .createType( + '(String, String, {"nonce":"U256","vara_token_id":"[u8;32]","amount":"U256","sender":"[u8;32]","receiver":"H160"})', + message.payload, + )[2] + .toJSON() as unknown as { + nonce: number | string | bigint; + vara_token_id: ActorId; + amount: number | string | bigint; + sender: ActorId; + receiver: H160; + }, + ); + } + }); } } diff --git a/frontend/src/features/history/utils/filters.ts b/frontend/src/features/history/utils/filters.ts index b9ee4df5..82773363 100644 --- a/frontend/src/features/history/utils/filters.ts +++ b/frontend/src/features/history/utils/filters.ts @@ -4,7 +4,7 @@ import { ActorId, H160 } from 'sails-js'; const getLastDaysISOTimestamp = (daysCount: number) => new Date(Date.now() - daysCount * 24 * 60 * 60 * 1000).toISOString(); -const getAssetOptions = (addresses: [ActorId, H160][], symbols: Record) => { +const getAssetOptions = (addresses: [ActorId, H160, 'ethereum' | 'gear'][], symbols: Record) => { const options = [] as { label: string; value: string }[]; for (const pair of addresses) { diff --git a/frontend/src/features/swap/consts/vara.ts b/frontend/src/features/swap/consts/vara.ts index 91579c9b..558a2508 100644 --- a/frontend/src/features/swap/consts/vara.ts +++ b/frontend/src/features/swap/consts/vara.ts @@ -1,16 +1,13 @@ const SERVICE_NAME = { BRIDGING_PAYMENT: 'bridgingPayment', - VFT_GATEWAY: 'vftGateway', VFT: 'vft', } as const; const FUNCTION_NAME = { - REQUEST_TO_GATEWAY: 'requestToGateway', APPROVE: 'approve', } as const; const QUERY_NAME = { - VFT_GATEWAY_ADDRESS: 'vftGatewayAddress', FT_ADDRESSES: 'varaToEthAddresses', BALANCE: 'balanceOf', DECIMALS: 'decimals', diff --git a/frontend/src/features/swap/hooks/vara/use-handle-vara-submit.ts b/frontend/src/features/swap/hooks/vara/use-handle-vara-submit.ts index 5ea36c30..2df92cb0 100644 --- a/frontend/src/features/swap/hooks/vara/use-handle-vara-submit.ts +++ b/frontend/src/features/swap/hooks/vara/use-handle-vara-submit.ts @@ -17,7 +17,7 @@ function useSendBridgingPaymentRequest() { return useSendProgramTransaction({ program, serviceName: SERVICE_NAME.BRIDGING_PAYMENT, - functionName: FUNCTION_NAME.REQUEST_TO_GATEWAY, + functionName: 'makeRequest', }); } diff --git a/frontend/src/features/swap/utils.ts b/frontend/src/features/swap/utils.ts index f9806ff7..d3228dc1 100644 --- a/frontend/src/features/swap/utils.ts +++ b/frontend/src/features/swap/utils.ts @@ -24,7 +24,10 @@ const getAmountSchema = ( .refine(() => feeValue <= accountBalanceValue, { message: ERROR_MESSAGE.NO_ACCOUNT_BALANCE }); }; -const getOptions = (addresses: [ActorId, H160][] | undefined, symbols: Record | undefined) => { +const getOptions = ( + addresses: [ActorId, H160, 'ethereum' | 'gear'][] | undefined, + symbols: Record | undefined, +) => { const varaOptions: { label: string; value: string }[] = []; const ethOptions: { label: string; value: string }[] = []; diff --git a/frontend/src/hooks/tokens/use-ft-addresses.ts b/frontend/src/hooks/tokens/use-ft-addresses.ts index 7ab3e313..eb20cd01 100644 --- a/frontend/src/hooks/tokens/use-ft-addresses.ts +++ b/frontend/src/hooks/tokens/use-ft-addresses.ts @@ -1,7 +1,7 @@ import { HexString } from '@gear-js/api'; import { useProgram, useProgramQuery } from '@gear-js/react-hooks'; -import { BridgingPaymentProgram, BRIDGING_PAYMENT_CONTRACT_ADDRESS, VftGatewayProgram } from '@/consts'; +import { BridgingPaymentProgram, BRIDGING_PAYMENT_CONTRACT_ADDRESS, VftManagerProgram } from '@/consts'; function useFTAddresses() { const { data: program } = useProgram({ @@ -9,21 +9,21 @@ function useFTAddresses() { id: BRIDGING_PAYMENT_CONTRACT_ADDRESS, }); - const { data: vftGatewayAddress } = useProgramQuery({ + const { data: vftManagerAddress } = useProgramQuery({ program, serviceName: 'bridgingPayment', - functionName: 'vftGatewayAddress', + functionName: 'vftManagerAddress', args: [], }); - const { data: vftGatewayProgram } = useProgram({ - library: VftGatewayProgram, - id: vftGatewayAddress?.toString() as HexString, + const { data: vftManagerProgram } = useProgram({ + library: VftManagerProgram, + id: vftManagerAddress?.toString() as HexString, }); return useProgramQuery({ - program: vftGatewayProgram, - serviceName: 'vftGateway', + program: vftManagerProgram, + serviceName: 'vftManager', functionName: 'varaToEthAddresses', args: [], }); diff --git a/frontend/src/hooks/tokens/use-ft-decimals.ts b/frontend/src/hooks/tokens/use-ft-decimals.ts index f06d8fb2..5b962ba8 100644 --- a/frontend/src/hooks/tokens/use-ft-decimals.ts +++ b/frontend/src/hooks/tokens/use-ft-decimals.ts @@ -7,7 +7,7 @@ import { readContract } from 'wagmi/actions'; import { VftProgram, FUNGIBLE_TOKEN_ABI } from '@/consts'; -function useFTDecimals(addresses: [ActorId, H160][] | undefined) { +function useFTDecimals(addresses: [ActorId, H160, 'ethereum' | 'gear'][] | undefined) { const { api, isApiReady } = useApi(); const wagmiConfig = useConfig(); diff --git a/frontend/src/hooks/tokens/use-ft-symbols.ts b/frontend/src/hooks/tokens/use-ft-symbols.ts index f99aec15..ba1483f3 100644 --- a/frontend/src/hooks/tokens/use-ft-symbols.ts +++ b/frontend/src/hooks/tokens/use-ft-symbols.ts @@ -7,7 +7,7 @@ import { readContract } from 'wagmi/actions'; import { VftProgram, FUNGIBLE_TOKEN_ABI } from '@/consts'; -function useFTSymbols(addresses: [ActorId, H160][] | undefined) { +function useFTSymbols(addresses: [ActorId, H160, 'ethereum' | 'gear'][] | undefined) { const { api, isApiReady } = useApi(); const wagmiConfig = useConfig();