diff --git a/src/chains/evm/common/constants/abi/wormhole-data-adapter-abi.ts b/src/chains/evm/common/constants/abi/wormhole-data-adapter-abi.ts new file mode 100644 index 0000000..ea096ef --- /dev/null +++ b/src/chains/evm/common/constants/abi/wormhole-data-adapter-abi.ts @@ -0,0 +1,605 @@ +export const WormholeDataAdapterAbi = [ + { + inputs: [ + { internalType: "address", name: "admin", type: "address" }, + { + internalType: "contract IWormholeRelayer", + name: "_wormholeRelayer", + type: "address", + }, + { + internalType: "contract IBridgeRouter", + name: "_bridgeRouter", + type: "address", + }, + { internalType: "address", name: "_refundAddress", type: "address" }, + ], + stateMutability: "nonpayable", + type: "constructor", + }, + { inputs: [], name: "AccessControlBadConfirmation", type: "error" }, + { + inputs: [{ internalType: "uint48", name: "schedule", type: "uint48" }], + name: "AccessControlEnforcedDefaultAdminDelay", + type: "error", + }, + { inputs: [], name: "AccessControlEnforcedDefaultAdminRules", type: "error" }, + { + inputs: [ + { internalType: "address", name: "defaultAdmin", type: "address" }, + ], + name: "AccessControlInvalidDefaultAdmin", + type: "error", + }, + { + inputs: [ + { internalType: "address", name: "account", type: "address" }, + { internalType: "bytes32", name: "neededRole", type: "bytes32" }, + ], + name: "AccessControlUnauthorizedAccount", + type: "error", + }, + { + inputs: [{ internalType: "uint16", name: "chainId", type: "uint16" }], + name: "ChainAlreadyAdded", + type: "error", + }, + { + inputs: [{ internalType: "uint16", name: "chainId", type: "uint16" }], + name: "ChainUnavailable", + type: "error", + }, + { inputs: [], name: "EmptyExtraArgs", type: "error" }, + { + inputs: [{ internalType: "address", name: "router", type: "address" }], + name: "InvalidBridgeRouter", + type: "error", + }, + { + inputs: [{ internalType: "uint64", name: "finalityLevel", type: "uint64" }], + name: "InvalidFinalityLevel", + type: "error", + }, + { + inputs: [ + { internalType: "bytes32", name: "sourceAddress", type: "bytes32" }, + ], + name: "InvalidMessageSender", + type: "error", + }, + { + inputs: [ + { internalType: "uint256", name: "expected", type: "uint256" }, + { internalType: "uint256", name: "actual", type: "uint256" }, + ], + name: "InvalidReceivedAmount", + type: "error", + }, + { + inputs: [{ internalType: "bytes32", name: "token", type: "bytes32" }], + name: "InvalidTokenAddress", + type: "error", + }, + { + inputs: [{ internalType: "address", name: "sender", type: "address" }], + name: "InvalidWormholeRelayer", + type: "error", + }, + { + inputs: [ + { internalType: "uint8", name: "bits", type: "uint8" }, + { internalType: "uint256", name: "value", type: "uint256" }, + ], + name: "SafeCastOverflowedUintDowncast", + type: "error", + }, + { inputs: [], name: "UnsupportedExtraArgs", type: "error" }, + { + inputs: [{ internalType: "uint64", name: "finalityLevel", type: "uint64" }], + name: "UnsupportedFinalityLevel", + type: "error", + }, + { + anonymous: false, + inputs: [], + name: "DefaultAdminDelayChangeCanceled", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: "uint48", + name: "newDelay", + type: "uint48", + }, + { + indexed: false, + internalType: "uint48", + name: "effectSchedule", + type: "uint48", + }, + ], + name: "DefaultAdminDelayChangeScheduled", + type: "event", + }, + { + anonymous: false, + inputs: [], + name: "DefaultAdminTransferCanceled", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: "address", + name: "newAdmin", + type: "address", + }, + { + indexed: false, + internalType: "uint48", + name: "acceptSchedule", + type: "uint48", + }, + ], + name: "DefaultAdminTransferScheduled", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: "bytes32", + name: "messageId", + type: "bytes32", + }, + ], + name: "ReceiveMessage", + type: "event", + }, + { + anonymous: false, + inputs: [ + { indexed: true, internalType: "bytes32", name: "role", type: "bytes32" }, + { + indexed: true, + internalType: "bytes32", + name: "previousAdminRole", + type: "bytes32", + }, + { + indexed: true, + internalType: "bytes32", + name: "newAdminRole", + type: "bytes32", + }, + ], + name: "RoleAdminChanged", + type: "event", + }, + { + anonymous: false, + inputs: [ + { indexed: true, internalType: "bytes32", name: "role", type: "bytes32" }, + { + indexed: true, + internalType: "address", + name: "account", + type: "address", + }, + { + indexed: true, + internalType: "address", + name: "sender", + type: "address", + }, + ], + name: "RoleGranted", + type: "event", + }, + { + anonymous: false, + inputs: [ + { indexed: true, internalType: "bytes32", name: "role", type: "bytes32" }, + { + indexed: true, + internalType: "address", + name: "account", + type: "address", + }, + { + indexed: true, + internalType: "address", + name: "sender", + type: "address", + }, + ], + name: "RoleRevoked", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + components: [ + { + components: [ + { internalType: "uint16", name: "adapterId", type: "uint16" }, + { + internalType: "uint16", + name: "returnAdapterId", + type: "uint16", + }, + { + internalType: "uint256", + name: "receiverValue", + type: "uint256", + }, + { internalType: "uint256", name: "gasLimit", type: "uint256" }, + { + internalType: "uint256", + name: "returnGasLimit", + type: "uint256", + }, + ], + internalType: "struct Messages.MessageParams", + name: "params", + type: "tuple", + }, + { internalType: "bytes32", name: "sender", type: "bytes32" }, + { + internalType: "uint16", + name: "destinationChainId", + type: "uint16", + }, + { internalType: "bytes32", name: "handler", type: "bytes32" }, + { internalType: "bytes", name: "payload", type: "bytes" }, + { internalType: "uint64", name: "finalityLevel", type: "uint64" }, + { internalType: "bytes", name: "extraArgs", type: "bytes" }, + ], + indexed: false, + internalType: "struct Messages.MessageToSend", + name: "message", + type: "tuple", + }, + ], + name: "SendMessage", + type: "event", + }, + { + inputs: [], + name: "DEFAULT_ADMIN_ROLE", + outputs: [{ internalType: "bytes32", name: "", type: "bytes32" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "MANAGER_ROLE", + outputs: [{ internalType: "bytes32", name: "", type: "bytes32" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "acceptDefaultAdminTransfer", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { internalType: "uint16", name: "folksChainId", type: "uint16" }, + { internalType: "uint16", name: "wormholeChainId", type: "uint16" }, + { internalType: "bytes32", name: "adapterAddress", type: "bytes32" }, + ], + name: "addChain", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [{ internalType: "address", name: "newAdmin", type: "address" }], + name: "beginDefaultAdminTransfer", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [], + name: "bridgeRouter", + outputs: [ + { internalType: "contract IBridgeRouter", name: "", type: "address" }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "cancelDefaultAdminTransfer", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [{ internalType: "uint48", name: "newDelay", type: "uint48" }], + name: "changeDefaultAdminDelay", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [], + name: "defaultAdmin", + outputs: [{ internalType: "address", name: "", type: "address" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "defaultAdminDelay", + outputs: [{ internalType: "uint48", name: "", type: "uint48" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "defaultAdminDelayIncreaseWait", + outputs: [{ internalType: "uint48", name: "", type: "uint48" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [{ internalType: "uint16", name: "chainId", type: "uint16" }], + name: "getChainAdapter", + outputs: [ + { internalType: "uint16", name: "wormholeChainId", type: "uint16" }, + { internalType: "bytes32", name: "adapterAddress", type: "bytes32" }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [{ internalType: "bytes32", name: "role", type: "bytes32" }], + name: "getRoleAdmin", + outputs: [{ internalType: "bytes32", name: "", type: "bytes32" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { + components: [ + { + components: [ + { internalType: "uint16", name: "adapterId", type: "uint16" }, + { + internalType: "uint16", + name: "returnAdapterId", + type: "uint16", + }, + { + internalType: "uint256", + name: "receiverValue", + type: "uint256", + }, + { internalType: "uint256", name: "gasLimit", type: "uint256" }, + { + internalType: "uint256", + name: "returnGasLimit", + type: "uint256", + }, + ], + internalType: "struct Messages.MessageParams", + name: "params", + type: "tuple", + }, + { internalType: "bytes32", name: "sender", type: "bytes32" }, + { + internalType: "uint16", + name: "destinationChainId", + type: "uint16", + }, + { internalType: "bytes32", name: "handler", type: "bytes32" }, + { internalType: "bytes", name: "payload", type: "bytes" }, + { internalType: "uint64", name: "finalityLevel", type: "uint64" }, + { internalType: "bytes", name: "extraArgs", type: "bytes" }, + ], + internalType: "struct Messages.MessageToSend", + name: "message", + type: "tuple", + }, + ], + name: "getSendFee", + outputs: [{ internalType: "uint256", name: "fee", type: "uint256" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { internalType: "bytes32", name: "role", type: "bytes32" }, + { internalType: "address", name: "account", type: "address" }, + ], + name: "grantRole", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { internalType: "bytes32", name: "role", type: "bytes32" }, + { internalType: "address", name: "account", type: "address" }, + ], + name: "hasRole", + outputs: [{ internalType: "bool", name: "", type: "bool" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [{ internalType: "uint16", name: "chainId", type: "uint16" }], + name: "isChainAvailable", + outputs: [{ internalType: "bool", name: "", type: "bool" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "owner", + outputs: [{ internalType: "address", name: "", type: "address" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "pendingDefaultAdmin", + outputs: [ + { internalType: "address", name: "newAdmin", type: "address" }, + { internalType: "uint48", name: "schedule", type: "uint48" }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "pendingDefaultAdminDelay", + outputs: [ + { internalType: "uint48", name: "newDelay", type: "uint48" }, + { internalType: "uint48", name: "schedule", type: "uint48" }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { internalType: "bytes", name: "payload", type: "bytes" }, + { internalType: "bytes[]", name: "", type: "bytes[]" }, + { internalType: "bytes32", name: "sourceAddress", type: "bytes32" }, + { internalType: "uint16", name: "sourceChain", type: "uint16" }, + { internalType: "bytes32", name: "deliveryHash", type: "bytes32" }, + ], + name: "receiveWormholeMessages", + outputs: [], + stateMutability: "payable", + type: "function", + }, + { + inputs: [], + name: "refundAddress", + outputs: [{ internalType: "address", name: "", type: "address" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [{ internalType: "uint16", name: "folksChainId", type: "uint16" }], + name: "removeChain", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { internalType: "bytes32", name: "role", type: "bytes32" }, + { internalType: "address", name: "account", type: "address" }, + ], + name: "renounceRole", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { internalType: "bytes32", name: "role", type: "bytes32" }, + { internalType: "address", name: "account", type: "address" }, + ], + name: "revokeRole", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [], + name: "rollbackDefaultAdminDelay", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { + components: [ + { + components: [ + { internalType: "uint16", name: "adapterId", type: "uint16" }, + { + internalType: "uint16", + name: "returnAdapterId", + type: "uint16", + }, + { + internalType: "uint256", + name: "receiverValue", + type: "uint256", + }, + { internalType: "uint256", name: "gasLimit", type: "uint256" }, + { + internalType: "uint256", + name: "returnGasLimit", + type: "uint256", + }, + ], + internalType: "struct Messages.MessageParams", + name: "params", + type: "tuple", + }, + { internalType: "bytes32", name: "sender", type: "bytes32" }, + { + internalType: "uint16", + name: "destinationChainId", + type: "uint16", + }, + { internalType: "bytes32", name: "handler", type: "bytes32" }, + { internalType: "bytes", name: "payload", type: "bytes" }, + { internalType: "uint64", name: "finalityLevel", type: "uint64" }, + { internalType: "bytes", name: "extraArgs", type: "bytes" }, + ], + internalType: "struct Messages.MessageToSend", + name: "message", + type: "tuple", + }, + ], + name: "sendMessage", + outputs: [], + stateMutability: "payable", + type: "function", + }, + { + inputs: [ + { internalType: "address", name: "_refundAddress", type: "address" }, + ], + name: "setRefundAddress", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [{ internalType: "bytes4", name: "interfaceId", type: "bytes4" }], + name: "supportsInterface", + outputs: [{ internalType: "bool", name: "", type: "bool" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "wormholeRelayer", + outputs: [ + { internalType: "contract IWormholeRelayer", name: "", type: "address" }, + ], + stateMutability: "view", + type: "function", + }, +] as const; diff --git a/src/chains/evm/common/utils/chain.ts b/src/chains/evm/common/utils/chain.ts index 0f86bbf..2979bdb 100644 --- a/src/chains/evm/common/utils/chain.ts +++ b/src/chains/evm/common/utils/chain.ts @@ -3,12 +3,12 @@ import { EVM_FOLKS_CHAIN_ID } from "../constants/chain.js"; import type { EvmChainId } from "../types/chain.js"; import type { Account, Address, WalletClient } from "viem"; -export function getSignerAddress(signer: WalletClient): Address { +export function getEvmSignerAddress(signer: WalletClient): Address { if (signer.account?.address) return signer.account.address; throw new Error("EVM Signer address is not set"); } -export function getSignerAccount(signer: WalletClient): Account { +export function getEvmSignerAccount(signer: WalletClient): Account { if (signer.account) return signer.account; throw new Error("EVM Signer account is not set"); } diff --git a/src/chains/evm/common/utils/contract.ts b/src/chains/evm/common/utils/contract.ts index fd214d0..73deed6 100644 --- a/src/chains/evm/common/utils/contract.ts +++ b/src/chains/evm/common/utils/contract.ts @@ -3,10 +3,12 @@ import { getContract } from "viem"; import { ChainType } from "../../../../common/types/chain.js"; import { convertFromGenericAddress } from "../../../../common/utils/address.js"; import { ERC20Abi } from "../constants/abi/erc-20-abi.js"; +import { WormholeDataAdapterAbi } from "../constants/abi/wormhole-data-adapter-abi.js"; -import { getSignerAccount, getSignerAddress } from "./chain.js"; +import { getEvmSignerAccount, getEvmSignerAddress } from "./chain.js"; import type { GenericAddress } from "../../../../common/types/chain.js"; +import type { GetReadContractReturnType } from "../types/contract.js"; import type { Address, Client, WalletClient } from "viem"; export function getERC20Contract( @@ -30,14 +32,25 @@ export async function sendERC20Approve( ) { const erc20 = getERC20Contract(provider, address, signer); const allowance = await erc20.read.allowance([ - getSignerAddress(signer), + getEvmSignerAddress(signer), receiver, ]); // approve if not enough if (allowance < amount) return await erc20.write.approve([receiver, BigInt(amount)], { - account: getSignerAccount(signer), + account: getEvmSignerAccount(signer), chain: signer.chain, }); } + +export function getWormholeDataAdapterContract( + provider: Client, + address: Address, +): GetReadContractReturnType { + return getContract({ + abi: WormholeDataAdapterAbi, + address: convertFromGenericAddress(address, ChainType.EVM), + client: { public: provider }, + }); +} diff --git a/src/chains/evm/common/utils/gmp.ts b/src/chains/evm/common/utils/gmp.ts new file mode 100644 index 0000000..867c46e --- /dev/null +++ b/src/chains/evm/common/utils/gmp.ts @@ -0,0 +1,27 @@ +import { concat } from "viem"; + +import { + UINT16_LENGTH, + UINT256_LENGTH, +} from "../../../../common/constants/bytes.js"; +import { convertNumberToBytes } from "../../../../common/utils/bytes.js"; + +import type { GenericAddress } from "../../../../common/types/chain.js"; +import type { AdapterType } from "../../../../common/types/message.js"; +import type { Hex } from "viem"; + +export function encodeWormholeEvmPayloadWithMetadata( + returnAdapterId: AdapterType, + returnGasLimit: bigint, + sender: GenericAddress, + handler: GenericAddress, + payload: Hex, +): Hex { + return concat([ + convertNumberToBytes(returnAdapterId, UINT16_LENGTH), + convertNumberToBytes(returnGasLimit, UINT256_LENGTH), + sender, + handler, + payload, + ]); +} diff --git a/src/chains/evm/common/utils/message.ts b/src/chains/evm/common/utils/message.ts index e77da3f..8a168bc 100644 --- a/src/chains/evm/common/utils/message.ts +++ b/src/chains/evm/common/utils/message.ts @@ -1,40 +1,44 @@ import { concat, isHex } from "viem"; import { + BYTES32_LENGTH, UINT16_LENGTH, UINT256_LENGTH, UINT8_LENGTH, } from "../../../../common/constants/bytes.js"; import { FINALITY } from "../../../../common/constants/message.js"; -import { ChainType } from "../../../../common/types/chain.js"; import { Action } from "../../../../common/types/message.js"; import { TokenType } from "../../../../common/types/token.js"; import { - convertToGenericAddress, getRandomGenericAddress, isGenericAddress, } from "../../../../common/utils/address.js"; import { convertBooleanToByte, convertNumberToBytes, + getRandomBytes, } from "../../../../common/utils/bytes.js"; import { exhaustiveCheck } from "../../../../utils/exhaustive-check.js"; +import { getWormholeDataAdapterContract } from "./contract.js"; +import { encodeWormholeEvmPayloadWithMetadata } from "./gmp.js"; + import type { GenericAddress } from "../../../../common/types/chain.js"; import type { MessageAdapters, + MessageBuilderParams, MessageParams, MessageToSend, - MessageToSendBuilderParams, + OptionalFeeParams, } from "../../../../common/types/message.js"; -import type { Hex } from "viem"; +import type { Client, Hex } from "viem"; export const DEFAULT_MESSAGE_PARAMS = ( adapters: MessageAdapters, ): MessageParams => ({ ...adapters, receiverValue: BigInt(0), - gasLimit: BigInt(30000), + gasLimit: BigInt(0), returnGasLimit: BigInt(0), }); @@ -42,7 +46,7 @@ export function buildMessagePayload( action: Action, accountId: Hex, userAddr: GenericAddress, - data: string, + data: Hex, ): Hex { if (!isGenericAddress(accountId)) throw Error("Unknown account id format"); if (!isGenericAddress(userAddr)) throw Error("Unknown user address format"); @@ -95,8 +99,89 @@ export function buildSendTokenExtraArgsWhenAdding( return extraArgsToBytes(spokeTokenAddress, hubPoolAddress, amount); } +export function buildEvmMessageData( + messageToSendBuilderParams: MessageBuilderParams, +): Hex { + const { action, data } = messageToSendBuilderParams; + switch (action) { + case Action.CreateAccount: { + return data; + } + case Action.InviteAddress: { + return concat([ + convertNumberToBytes(data.folksChainIdToInvite, UINT16_LENGTH), + data.addressToInvite, + ]); + } + case Action.AcceptInviteAddress: { + return data; + } + case Action.UnregisterAddress: { + return convertNumberToBytes(data.folksChainIdToUnregister, UINT16_LENGTH); + } + case Action.AddDelegate: { + throw new Error("Not implemented yet: Action.AddDelegate case"); + } + case Action.RemoveDelegate: { + throw new Error("Not implemented yet: Action.RemoveDelegate case"); + } + case Action.CreateLoan: { + return concat([ + data.loanId, + convertNumberToBytes(data.loanTypeId, UINT16_LENGTH), + ]); + } + case Action.DeleteLoan: { + return data.loanId; + } + case Action.Deposit: { + return concat([ + data.loanId, + convertNumberToBytes(data.poolId, UINT8_LENGTH), + convertNumberToBytes(data.amount, UINT256_LENGTH), + ]); + } + case Action.DepositFToken: { + throw new Error("Not implemented yet: Action.DepositFToken case"); + } + case Action.Withdraw: { + return concat([ + data.loanId, + convertNumberToBytes(data.poolId, UINT8_LENGTH), + convertNumberToBytes(data.receiverFolksChainId, UINT16_LENGTH), + convertNumberToBytes(data.amount, UINT256_LENGTH), + convertBooleanToByte(data.isFAmount), + ]); + } + case Action.WithdrawFToken: { + throw new Error("Not implemented yet: Action.WithdrawFToken case"); + } + case Action.Borrow: { + throw new Error("Not implemented yet: Action.Borrow case"); + } + case Action.Repay: { + throw new Error("Not implemented yet: Action.Repay case"); + } + case Action.RepayWithCollateral: { + throw new Error("Not implemented yet: Action.RepayWithCollateral case"); + } + case Action.Liquidate: { + throw new Error("Not implemented yet: Action.Liquidate case"); + } + case Action.SwitchBorrowType: { + throw new Error("Not implemented yet: Action.SwitchBorrowType case"); + } + case Action.SendToken: { + throw new Error("Not implemented yet: Action.SendToken case"); + } + default: + return exhaustiveCheck(action); + } +} + export function buildEvmMessageToSend( - messageToSendBuilderParams: MessageToSendBuilderParams, + messageToSendBuilderParams: MessageBuilderParams, + feeParams: OptionalFeeParams, ): MessageToSend { const { accountId, @@ -105,12 +190,12 @@ export function buildEvmMessageToSend( destinationChainId, handler, action, - data, extraArgs, } = messageToSendBuilderParams; + const data = buildEvmMessageData(messageToSendBuilderParams); + const params = { ...DEFAULT_MESSAGE_PARAMS(adapters), ...feeParams }; switch (action) { case Action.CreateAccount: { - const params = DEFAULT_MESSAGE_PARAMS(adapters); const message: MessageToSend = { params, sender, @@ -128,7 +213,6 @@ export function buildEvmMessageToSend( return message; } case Action.InviteAddress: { - const params = DEFAULT_MESSAGE_PARAMS(adapters); const message: MessageToSend = { params, sender, @@ -138,13 +222,7 @@ export function buildEvmMessageToSend( Action.InviteAddress, accountId, getRandomGenericAddress(), - concat([ - convertNumberToBytes(data.folksChainIdToInvite, UINT16_LENGTH), - convertToGenericAddress( - data.addressToInvite, - ChainType.EVM, - ), - ]), + data, ), finalityLevel: FINALITY.IMMEDIATE, extraArgs: "0x", @@ -152,7 +230,6 @@ export function buildEvmMessageToSend( return message; } case Action.AcceptInviteAddress: { - const params = DEFAULT_MESSAGE_PARAMS(adapters); const message: MessageToSend = { params, sender, @@ -170,7 +247,6 @@ export function buildEvmMessageToSend( return message; } case Action.UnregisterAddress: { - const params = DEFAULT_MESSAGE_PARAMS(adapters); const message: MessageToSend = { params, sender, @@ -180,7 +256,7 @@ export function buildEvmMessageToSend( Action.UnregisterAddress, accountId, getRandomGenericAddress(), - convertNumberToBytes(data.folksChainIdToUnregister, UINT16_LENGTH), + data, ), finalityLevel: FINALITY.IMMEDIATE, extraArgs, @@ -194,7 +270,6 @@ export function buildEvmMessageToSend( throw new Error("Not implemented yet: Action.RemoveDelegate case"); } case Action.CreateLoan: { - const params = DEFAULT_MESSAGE_PARAMS(adapters); const message: MessageToSend = { params, sender, @@ -204,10 +279,7 @@ export function buildEvmMessageToSend( Action.CreateLoan, accountId, getRandomGenericAddress(), - concat([ - data.loanId, - convertNumberToBytes(data.loanTypeId, UINT16_LENGTH), - ]), + data, ), finalityLevel: FINALITY.IMMEDIATE, extraArgs, @@ -215,7 +287,6 @@ export function buildEvmMessageToSend( return message; } case Action.DeleteLoan: { - const params = DEFAULT_MESSAGE_PARAMS(adapters); const message: MessageToSend = { params, sender, @@ -223,9 +294,9 @@ export function buildEvmMessageToSend( handler, payload: buildMessagePayload( Action.DeleteLoan, - data.accountId, + accountId, getRandomGenericAddress(), - data.loanId, + data, ), finalityLevel: FINALITY.IMMEDIATE, extraArgs, @@ -233,7 +304,6 @@ export function buildEvmMessageToSend( return message; } case Action.Deposit: { - const params = DEFAULT_MESSAGE_PARAMS(adapters); const message: MessageToSend = { params, sender, @@ -243,11 +313,7 @@ export function buildEvmMessageToSend( Action.Deposit, accountId, getRandomGenericAddress(), - concat([ - data.loanId, - convertNumberToBytes(data.poolId, UINT8_LENGTH), - convertNumberToBytes(data.amount, UINT256_LENGTH), - ]), + data, ), finalityLevel: FINALITY.FINALISED, extraArgs: buildSendTokenExtraArgsWhenAdding( @@ -263,10 +329,6 @@ export function buildEvmMessageToSend( throw new Error("Not implemented yet: Action.DepositFToken case"); } case Action.Withdraw: { - const params = { - ...DEFAULT_MESSAGE_PARAMS(adapters), - ...messageToSendBuilderParams.params, - }; const message: MessageToSend = { params, sender, @@ -276,13 +338,7 @@ export function buildEvmMessageToSend( Action.Withdraw, accountId, getRandomGenericAddress(), - concat([ - data.loanId, - convertNumberToBytes(data.poolId, UINT8_LENGTH), - convertNumberToBytes(data.receiverFolksChainId, UINT16_LENGTH), - convertNumberToBytes(data.amount, UINT256_LENGTH), - convertBooleanToByte(data.isFAmount), - ]), + data, ), finalityLevel: FINALITY.IMMEDIATE, extraArgs, @@ -314,3 +370,41 @@ export function buildEvmMessageToSend( return exhaustiveCheck(action); } } + +export async function estimateEVMWormholeDataGasLimit( + provider: Client, + messageBuilderParams: MessageBuilderParams, + receiverValue: bigint, + returnGasLimit: bigint, + sourceWormholeChainId: number, + wormholeRelayer: GenericAddress, + wormholeDataAdapterAddress: GenericAddress, + sourceWormholeDataAdapterAddress: GenericAddress, +) { + const messageId = getRandomBytes(BYTES32_LENGTH); + const wormholeDataAdapter = getWormholeDataAdapterContract( + provider, + wormholeDataAdapterAddress, + ); + return await wormholeDataAdapter.estimateGas.receiveWormholeMessages( + [ + encodeWormholeEvmPayloadWithMetadata( + messageBuilderParams.adapters.returnAdapterId, + returnGasLimit, + messageBuilderParams.sender, + messageBuilderParams.handler, + buildMessagePayload( + messageBuilderParams.action, + messageBuilderParams.accountId, + messageBuilderParams.userAddress, + buildEvmMessageData(messageBuilderParams), + ), + ), + [], + sourceWormholeDataAdapterAddress, + sourceWormholeChainId, + messageId, + ], + { value: receiverValue, account: wormholeRelayer }, + ); +} diff --git a/src/chains/evm/hub/utils/chain.ts b/src/chains/evm/hub/utils/chain.ts index 193a040..b12f6d2 100644 --- a/src/chains/evm/hub/utils/chain.ts +++ b/src/chains/evm/hub/utils/chain.ts @@ -5,6 +5,7 @@ import type { GenericAddress, NetworkType, } from "../../../../common/types/chain.js"; +import type { AdapterType } from "../../../../common/types/message.js"; import type { LoanType } from "../../../../common/types/module.js"; import type { FolksTokenId } from "../../../../common/types/token.js"; import type { HubChain } from "../types/chain.js"; @@ -55,3 +56,11 @@ export function getHubTokenAddress(hubTokenData: HubTokenData): GenericAddress { `Hub token address not found for folksTokenId: ${hubTokenData.folksTokenId}`, ); } + +export function getHubChainAdapterAddress( + network: NetworkType, + adapterType: AdapterType, +) { + const hubChain = getHubChain(network); + return hubChain.adapters[adapterType]; +} diff --git a/src/chains/evm/spoke/modules/folks-evm-account.ts b/src/chains/evm/spoke/modules/folks-evm-account.ts index d411f90..28c03cb 100644 --- a/src/chains/evm/spoke/modules/folks-evm-account.ts +++ b/src/chains/evm/spoke/modules/folks-evm-account.ts @@ -1,4 +1,4 @@ -import { getSignerAccount } from "../../common/utils/chain.js"; +import { getEvmSignerAccount } from "../../common/utils/chain.js"; import { getBridgeRouterSpokeContract, getSpokeCommonContract, @@ -58,14 +58,13 @@ export const prepare = { }, ); const returnReceiveGasLimit = BigInt(0); - const receiveGasLimit = BigInt(300000); // TODO return { adapters, adapterFee, returnAdapterFee, gasLimit, - receiveGasLimit, + receiveGasLimit: messageToSend.params.gasLimit, returnReceiveGasLimit, spokeCommonAddress, }; @@ -234,7 +233,7 @@ export const write = { }; return await spokeCommon.write.createAccount([params, accountId], { - account: getSignerAccount(signer), + account: getEvmSignerAccount(signer), chain: signer.chain, gas: gasLimit, value: adapterFee, @@ -275,7 +274,7 @@ export const write = { return await spokeCommon.write.inviteAddress( [params, accountId, folksChainIdToInvite, addressToInvite], { - account: getSignerAccount(signer), + account: getEvmSignerAccount(signer), chain: signer.chain, gasLimit: gasLimit, value: adapterFee, @@ -313,7 +312,7 @@ export const write = { }; return await spokeCommon.write.acceptInviteAddress([params, accountId], { - account: getSignerAccount(signer), + account: getEvmSignerAccount(signer), chain: signer.chain, gasLimit: gasLimit, value: adapterFee, @@ -353,7 +352,7 @@ export const write = { return await spokeCommon.write.unregisterAddress( [params, accountId, folksChainIdToUnregister], { - account: getSignerAccount(signer), + account: getEvmSignerAccount(signer), chain: signer.chain, gasLimit: gasLimit, value: adapterFee, diff --git a/src/chains/evm/spoke/modules/folks-evm-loan.ts b/src/chains/evm/spoke/modules/folks-evm-loan.ts index 17b000f..6ecb147 100644 --- a/src/chains/evm/spoke/modules/folks-evm-loan.ts +++ b/src/chains/evm/spoke/modules/folks-evm-loan.ts @@ -1,5 +1,5 @@ import { TokenType } from "../../../../common/types/token.js"; -import { getSignerAccount } from "../../common/utils/chain.js"; +import { getEvmSignerAccount } from "../../common/utils/chain.js"; import { sendERC20Approve } from "../../common/utils/contract.js"; import { getHubTokenData } from "../../hub/utils/chain.js"; import { @@ -266,7 +266,7 @@ export const write = { return await spokeCommon.write.createLoan( [params, accountId, loanId, loanTypeId], { - account: getSignerAccount(signer), + account: getEvmSignerAccount(signer), chain: signer.chain, gasLimit: gasLimit, value: adapterFee, @@ -305,7 +305,7 @@ export const write = { }; return await spokeCommon.write.deleteLoan([params, accountId, loanId], { - account: getSignerAccount(signer), + account: getEvmSignerAccount(signer), chain: signer.chain, gasLimit: gasLimit, value: adapterFee, @@ -354,7 +354,7 @@ export const write = { }; return await spokeToken.write.deposit([params, accountId, loanId, amount], { - account: getSignerAccount(signer), + account: getEvmSignerAccount(signer), chain: signer.chain, gasLimit: gasLimit, value: adapterFee, @@ -397,7 +397,7 @@ export const write = { return await spokeCommon.write.withdraw( [params, accountId, loanId, poolId, receiverChainId, amount, isFAmount], { - account: getSignerAccount(signer), + account: getEvmSignerAccount(signer), chain: signer.chain, gasLimit: gasLimit, value: adapterFee, diff --git a/src/common/constants/gmp.ts b/src/common/constants/gmp.ts new file mode 100644 index 0000000..21de947 --- /dev/null +++ b/src/common/constants/gmp.ts @@ -0,0 +1,31 @@ +import { ChainType } from "../types/chain.js"; +import { convertToGenericAddress } from "../utils/address.js"; + +import { FOLKS_CHAIN_ID } from "./chain.js"; + +import type { FolksChainId } from "../types/chain.js"; +import type { WormholeData } from "../types/gmp.js"; + +export const WORMHOLE_DATA: Partial> = { + [FOLKS_CHAIN_ID.AVALANCHE_FUJI]: { + wormholeChainId: 6, + wormholeRelayer: convertToGenericAddress( + "0xA3cF45939bD6260bcFe3D66bc73d60f19e49a8BB", + ChainType.EVM, + ), + }, + [FOLKS_CHAIN_ID.ETHEREUM_SEPOLIA]: { + wormholeChainId: 10002, + wormholeRelayer: convertToGenericAddress( + "0x7B1bD7a6b4E61c2a123AC6BC2cbfC614437D0470", + ChainType.EVM, + ), + }, + [FOLKS_CHAIN_ID.BASE_SEPOLIA]: { + wormholeChainId: 10004, + wormholeRelayer: convertToGenericAddress( + "0x93BAD53DDfB6132b0aC8E37f6029163E63372cEE", + ChainType.EVM, + ), + }, +}; diff --git a/src/common/types/core.ts b/src/common/types/core.ts index 41160c3..683d4ef 100644 --- a/src/common/types/core.ts +++ b/src/common/types/core.ts @@ -22,3 +22,10 @@ export type FolksCoreConfig = { network: NetworkType; provider: FolksCoreProvider; }; + +export type FolksEvmSigner = { + signer: EVMSigner; + chainType: ChainType.EVM; +}; + +export type FolksChainSigner = FolksEvmSigner; diff --git a/src/common/types/gmp.ts b/src/common/types/gmp.ts new file mode 100644 index 0000000..fa31217 --- /dev/null +++ b/src/common/types/gmp.ts @@ -0,0 +1,6 @@ +import type { GenericAddress } from "./chain.js"; + +export type WormholeData = { + wormholeChainId: number; + wormholeRelayer: GenericAddress; +}; diff --git a/src/common/types/message.ts b/src/common/types/message.ts index 7b98489..b89efa8 100644 --- a/src/common/types/message.ts +++ b/src/common/types/message.ts @@ -42,11 +42,15 @@ export type MessageAdapters = { returnAdapterId: AdapterType; }; -export type MessageParams = { +export type FeeParams = { receiverValue: bigint; gasLimit: bigint; returnGasLimit: bigint; -} & MessageAdapters; +}; + +export type MessageParams = FeeParams & MessageAdapters; + +export type OptionalFeeParams = Partial; export type MessageToSend = { params: MessageParams; @@ -154,7 +158,6 @@ export type DeleteLoanMessageDataParams = { export type WithdrawMessageDataParams = { action: Action.Withdraw; - params: Partial; data: WithdrawMessageData; extraArgs: DefaultExtraArgs; }; @@ -174,7 +177,8 @@ export type MessageDataParams = | DepositMessageDataParams | WithdrawMessageDataParams; -export type MessageToSendBuilderParams = { +export type MessageBuilderParams = { + userAddress: GenericAddress; accountId: Hex; adapters: MessageAdapters; sender: GenericAddress; diff --git a/src/common/utils/chain.ts b/src/common/utils/chain.ts index 7867ffa..bdb67ae 100644 --- a/src/common/utils/chain.ts +++ b/src/common/utils/chain.ts @@ -1,4 +1,13 @@ +import { getEvmSignerAddress } from "../../chains/evm/common/utils/chain.js"; +import { + getHubChainAdapterAddress, + isHubChain, +} from "../../chains/evm/hub/utils/chain.js"; +import { exhaustiveCheck } from "../../utils/exhaustive-check.js"; import { FOLKS_CHAIN, SPOKE_CHAIN } from "../constants/chain.js"; +import { ChainType } from "../types/chain.js"; + +import { convertToGenericAddress } from "./address.js"; import type { FolksChainId, @@ -7,6 +16,8 @@ import type { SpokeChain, GenericAddress, } from "../types/chain.js"; +import type { FolksChainSigner } from "../types/core.js"; +import type { AdapterType } from "../types/message.js"; import type { FolksTokenId, SpokeTokenData } from "../types/token.js"; export function getFolksChain( @@ -108,3 +119,41 @@ export function getSpokeTokenDataTokenAddress( `Token address not found for spokeTokenData for folks token: ${spokeTokenData.folksTokenId} in spoke: ${spokeTokenData.spokeAddress}`, ); } + +export function getSpokeChainAdapterAddress( + folksChainId: FolksChainId, + network: NetworkType, + adapterType: AdapterType, +): GenericAddress { + const spokeChain = getSpokeChain(folksChainId, network); + const adapterAddress = spokeChain.adapters[adapterType]; + if (adapterAddress) return adapterAddress; + throw new Error( + `Adapter ${adapterType} not found for spoke chain ${folksChainId}`, + ); +} + +export function getAdapterAddress( + folksChainId: FolksChainId, + network: NetworkType, + adapterType: AdapterType, +) { + if (isHubChain(folksChainId, network)) + return getHubChainAdapterAddress(network, adapterType); + return getSpokeChainAdapterAddress(folksChainId, network, adapterType); +} + +export function getSignerGenericAddress( + folksChainSigner: FolksChainSigner, +): GenericAddress { + const chainType = folksChainSigner.chainType; + switch (chainType) { + case ChainType.EVM: + return convertToGenericAddress( + getEvmSignerAddress(folksChainSigner.signer), + chainType, + ); + default: + return exhaustiveCheck(chainType); + } +} diff --git a/src/common/utils/messages.ts b/src/common/utils/messages.ts index 82bec6d..f7b993e 100644 --- a/src/common/utils/messages.ts +++ b/src/common/utils/messages.ts @@ -1,21 +1,105 @@ -import { buildEvmMessageToSend } from "../../chains/evm/common/utils/message.js"; +import { + buildEvmMessageToSend, + estimateEVMWormholeDataGasLimit, +} from "../../chains/evm/common/utils/message.js"; import { exhaustiveCheck } from "../../utils/exhaustive-check.js"; +import { WORMHOLE_DATA } from "../constants/gmp.js"; import { ChainType } from "../types/chain.js"; +import { AdapterType } from "../types/message.js"; +import { convertFromGenericAddress } from "./address.js"; +import { getFolksChain } from "./chain.js"; + +import type { + FolksChainId, + GenericAddress, + NetworkType, +} from "../types/chain.js"; +import type { FolksProvider } from "../types/core.js"; +import type { WormholeData } from "../types/gmp.js"; import type { + MessageAdapters, MessageToSend, - MessageToSendBuilderParams, + MessageBuilderParams, + OptionalFeeParams, } from "../types/message.js"; +import type { Client as EVMProvider } from "viem"; export function buildMessageToSend( chainType: ChainType, - messageToSendBuilderParams: MessageToSendBuilderParams, + messageToSendBuilderParams: MessageBuilderParams, + feeParams: OptionalFeeParams = {}, ): MessageToSend { switch (chainType) { case ChainType.EVM: { - return buildEvmMessageToSend(messageToSendBuilderParams); + return buildEvmMessageToSend(messageToSendBuilderParams, feeParams); } default: return exhaustiveCheck(chainType); } } + +export function getWormholeData(folksChainId: FolksChainId): WormholeData { + const wormholeData = WORMHOLE_DATA[folksChainId]; + if (wormholeData) return wormholeData; + throw new Error(`Wormhole data not found for folksChainId: ${folksChainId}`); +} + +export async function estimateReceiveGasLimit( + sourceFolksChainId: FolksChainId, + destFolksChainId: FolksChainId, + destFolksChainProvider: FolksProvider, + network: NetworkType, + adapters: MessageAdapters, + sourceAdapterAddress: GenericAddress, + destAdapterAddress: GenericAddress, + messageBuilderParams: MessageBuilderParams, + receiverValue = BigInt(0), + returnGasLimit = BigInt(0), +) { + const destFolksChain = getFolksChain(destFolksChainId, network); + switch (destFolksChain.chainType) { + case ChainType.EVM: + switch (adapters.adapterId) { + case AdapterType.WORMHOLE_DATA: { + const sourceWormholeChainId = + getWormholeData(sourceFolksChainId).wormholeChainId; + const wormholeRelayer = convertFromGenericAddress( + getWormholeData(destFolksChainId).wormholeRelayer, + ChainType.EVM, + ); + + return await estimateEVMWormholeDataGasLimit( + // eslint-disable-next-line @typescript-eslint/non-nullable-type-assertion-style + destFolksChainProvider as EVMProvider, + messageBuilderParams, + receiverValue, + returnGasLimit, + sourceWormholeChainId, + wormholeRelayer, + destAdapterAddress, + sourceAdapterAddress, + ); + } + case AdapterType.WORMHOLE_CCTP: { + throw new Error( + "Not implemented yet: AdapterType.WORMHOLE_CCTP case", + ); + } + case AdapterType.HUB: { + throw new Error("Not implemented yet: AdapterType.HUB case"); + } + case AdapterType.CCIP_DATA: { + throw new Error("Not implemented yet: AdapterType.CCIP_DATA case"); + } + case AdapterType.CCIP_TOKEN: { + throw new Error("Not implemented yet: AdapterType.CCIP_TOKEN case"); + } + default: + return exhaustiveCheck(adapters.adapterId); + } + + default: + return exhaustiveCheck(destFolksChain.chainType); + } +} diff --git a/src/xchain/modules/folks-account.ts b/src/xchain/modules/folks-account.ts index d7583f8..1b4099c 100644 --- a/src/xchain/modules/folks-account.ts +++ b/src/xchain/modules/folks-account.ts @@ -1,23 +1,33 @@ -import { getSignerAddress } from "../../chains/evm/common/utils/chain.js"; import { FolksHubAccount } from "../../chains/evm/hub/modules/index.js"; -import { getHubChain } from "../../chains/evm/hub/utils/chain.js"; +import { + getHubChain, + getHubChainAdapterAddress, +} from "../../chains/evm/hub/utils/chain.js"; import { FolksEvmAccount } from "../../chains/evm/spoke/modules/index.js"; import { ChainType } from "../../common/types/chain.js"; import { Action } from "../../common/types/message.js"; import { assertAdapterSupportsDataMessage } from "../../common/utils/adapter.js"; +import { convertFromGenericAddress } from "../../common/utils/address.js"; import { assertSpokeChainSupported, + getSignerGenericAddress, getSpokeChain, + getSpokeChainAdapterAddress, } from "../../common/utils/chain.js"; -import { buildMessageToSend } from "../../common/utils/messages.js"; +import { + buildMessageToSend, + estimateReceiveGasLimit, +} from "../../common/utils/messages.js"; import { exhaustiveCheck } from "../../utils/exhaustive-check.js"; import { FolksCore } from "../core/folks-core.js"; -import type { FolksChainId } from "../../common/types/chain.js"; +import type { FolksChainId, GenericAddress } from "../../common/types/chain.js"; import type { InviteAddressMessageData, MessageAdapters, + MessageBuilderParams, UnregisterAddressMessageData, + OptionalFeeParams, } from "../../common/types/message.js"; import type { PrepareAcceptInviteAddressCall, @@ -42,7 +52,13 @@ export const prepare = { ); const hubChain = getHubChain(folksChain.network); - const messageToSend = buildMessageToSend(folksChain.chainType, { + const userAddress = getSignerGenericAddress({ + signer: FolksCore.getFolksSigner().signer, + chainType: folksChain.chainType, + }); + + const messageBuilderParams: MessageBuilderParams = { + userAddress, accountId, adapters, action: Action.CreateAccount, @@ -51,13 +67,41 @@ export const prepare = { handler: hubChain.hubAddress, data: "0x", extraArgs: "0x", - }); + }; + const feeParams: OptionalFeeParams = {}; + + const sourceAdapterAddress = getSpokeChainAdapterAddress( + folksChain.folksChainId, + folksChain.network, + adapters.adapterId, + ); + const destAdapterAddress = getHubChainAdapterAddress( + folksChain.network, + adapters.adapterId, + ); + + feeParams.gasLimit = await estimateReceiveGasLimit( + folksChain.folksChainId, + hubChain.folksChainId, + FolksCore.getHubProvider(), + folksChain.network, + adapters, + sourceAdapterAddress, + destAdapterAddress, + messageBuilderParams, + ); + + const messageToSend = buildMessageToSend( + folksChain.chainType, + messageBuilderParams, + feeParams, + ); switch (folksChain.chainType) { case ChainType.EVM: return await FolksEvmAccount.prepare.createAccount( FolksCore.getProvider(folksChain.folksChainId), - getSignerAddress(FolksCore.getSigner()), + convertFromGenericAddress(userAddress, folksChain.chainType), messageToSend, accountId, adapters, @@ -71,7 +115,7 @@ export const prepare = { async inviteAddress( accountId: Hex, folksChainIdToInvite: FolksChainId, - addressToInvite: Address, + addressToInvite: GenericAddress, adapters: MessageAdapters, ) { const folksChain = FolksCore.getSelectedFolksChain(); @@ -87,11 +131,17 @@ export const prepare = { ); const hubChain = getHubChain(folksChain.network); + const userAddress = getSignerGenericAddress({ + signer: FolksCore.getFolksSigner().signer, + chainType: folksChain.chainType, + }); + const data: InviteAddressMessageData = { folksChainIdToInvite, addressToInvite, }; const messageToSend = buildMessageToSend(folksChain.chainType, { + userAddress, accountId, adapters, action: Action.InviteAddress, @@ -106,7 +156,7 @@ export const prepare = { case ChainType.EVM: return await FolksEvmAccount.prepare.inviteAddress( FolksCore.getProvider(folksChain.folksChainId), - getSignerAddress(FolksCore.getSigner()), + convertFromGenericAddress(userAddress, folksChain.chainType), messageToSend, accountId, folksChainIdToInvite, @@ -133,7 +183,13 @@ export const prepare = { ); const hubChain = getHubChain(folksChain.network); + const userAddress = getSignerGenericAddress({ + signer: FolksCore.getFolksSigner().signer, + chainType: folksChain.chainType, + }); + const messageToSend = buildMessageToSend(folksChain.chainType, { + userAddress, accountId, adapters, action: Action.AcceptInviteAddress, @@ -148,7 +204,7 @@ export const prepare = { case ChainType.EVM: return await FolksEvmAccount.prepare.acceptInvite( FolksCore.getProvider(folksChain.folksChainId), - getSignerAddress(FolksCore.getSigner()), + convertFromGenericAddress(userAddress, folksChain.chainType), messageToSend, accountId, adapters, @@ -177,10 +233,16 @@ export const prepare = { ); const hubChain = getHubChain(folksChain.network); + const userAddress = getSignerGenericAddress({ + signer: FolksCore.getFolksSigner().signer, + chainType: folksChain.chainType, + }); + const data: UnregisterAddressMessageData = { folksChainIdToUnregister, }; const messageToSend = buildMessageToSend(folksChain.chainType, { + userAddress, accountId, adapters, action: Action.UnregisterAddress, @@ -195,7 +257,7 @@ export const prepare = { case ChainType.EVM: return await FolksEvmAccount.prepare.unregisterAddress( FolksCore.getProvider(folksChain.folksChainId), - getSignerAddress(FolksCore.getSigner()), + convertFromGenericAddress(userAddress, folksChain.chainType), messageToSend, accountId, folksChainIdToUnregister, diff --git a/src/xchain/modules/folks-loan.ts b/src/xchain/modules/folks-loan.ts index 44b8311..e5d6c7f 100644 --- a/src/xchain/modules/folks-loan.ts +++ b/src/xchain/modules/folks-loan.ts @@ -1,4 +1,3 @@ -import { getSignerAddress } from "../../chains/evm/common/utils/chain.js"; import { FolksHubLoan } from "../../chains/evm/hub/modules/index.js"; import { assertLoanTypeSupported, @@ -12,9 +11,11 @@ import { assertAdapterSupportsDataMessage, assertAdapterSupportsTokenMessage, } from "../../common/utils/adapter.js"; +import { convertFromGenericAddress } from "../../common/utils/address.js"; import { assertSpokeChainSupportFolksToken, assertSpokeChainSupported, + getSignerGenericAddress, getSpokeChain, getSpokeTokenData, getSpokeTokenDataTokenAddress, @@ -30,6 +31,7 @@ import type { DepositExtraArgs, DepositMessageData, MessageAdapters, + OptionalFeeParams, WithdrawMessageData, } from "../../common/types/message.js"; import type { @@ -60,11 +62,17 @@ export const prepare = { ); const hubChain = getHubChain(folksChain.network); + const userAddress = getSignerGenericAddress({ + signer: FolksCore.getFolksSigner().signer, + chainType: folksChain.chainType, + }); + const data: CreateLoanMessageData = { loanId, loanTypeId, }; const messageToSend = buildMessageToSend(folksChain.chainType, { + userAddress, accountId, adapters, action: Action.CreateLoan, @@ -79,7 +87,7 @@ export const prepare = { case ChainType.EVM: return await FolksEvmLoan.prepare.createLoan( FolksCore.getProvider(folksChain.folksChainId), - getSignerAddress(FolksCore.getSigner()), + convertFromGenericAddress(userAddress, folksChain.chainType), messageToSend, accountId, loanId, @@ -105,11 +113,17 @@ export const prepare = { ); const hubChain = getHubChain(folksChain.network); + const userAddress = getSignerGenericAddress({ + signer: FolksCore.getFolksSigner().signer, + chainType: folksChain.chainType, + }); + const data: DeleteLoanMessageData = { accountId, loanId, }; const messageToSend = buildMessageToSend(folksChain.chainType, { + userAddress, accountId, adapters, action: Action.DeleteLoan, @@ -124,7 +138,7 @@ export const prepare = { case ChainType.EVM: return await FolksEvmLoan.prepare.deleteLoan( FolksCore.getProvider(folksChain.folksChainId), - getSignerAddress(FolksCore.getSigner()), + convertFromGenericAddress(userAddress, folksChain.chainType), messageToSend, accountId, loanId, @@ -165,6 +179,11 @@ export const prepare = { const spokeTokenData = getSpokeTokenData(spokeChain, folksTokenId); const hubTokenData = getHubTokenData(folksTokenId, folksChain.network); + const userAddress = getSignerGenericAddress({ + signer: FolksCore.getFolksSigner().signer, + chainType: folksChain.chainType, + }); + const data: DepositMessageData = { loanId, poolId: hubTokenData.poolId, @@ -177,6 +196,7 @@ export const prepare = { amount, }; const messageToSend = buildMessageToSend(folksChain.chainType, { + userAddress, accountId, adapters, action: Action.Deposit, @@ -191,7 +211,7 @@ export const prepare = { case ChainType.EVM: return await FolksEvmLoan.prepare.deposit( FolksCore.getProvider(folksChain.folksChainId), - getSignerAddress(FolksCore.getSigner()), + convertFromGenericAddress(userAddress, folksChain.chainType), messageToSend, accountId, loanId, @@ -253,6 +273,15 @@ export const prepare = { const hubTokenData = getHubTokenData(folksTokenId, folksChain.network); + const userAddress = getSignerGenericAddress({ + signer: FolksCore.getFolksSigner().signer, + chainType: folksChain.chainType, + }); + + const feeParams: OptionalFeeParams = { + receiverValue: await getReturnAdapterFees(), + }; + const data: WithdrawMessageData = { loanId, poolId: hubTokenData.poolId, @@ -260,23 +289,27 @@ export const prepare = { amount, isFAmount, }; - const messageToSend = buildMessageToSend(folksChain.chainType, { - accountId, - adapters, - action: Action.Withdraw, - sender: spokeChain.spokeCommonAddress, - destinationChainId: hubChain.folksChainId, - handler: hubChain.hubAddress, - params: { receiverValue: await getReturnAdapterFees() }, - data, - extraArgs: "0x", - }); + const messageToSend = buildMessageToSend( + folksChain.chainType, + { + userAddress, + accountId, + adapters, + action: Action.Withdraw, + sender: spokeChain.spokeCommonAddress, + destinationChainId: hubChain.folksChainId, + handler: hubChain.hubAddress, + data, + extraArgs: "0x", + }, + feeParams, + ); switch (folksChain.chainType) { case ChainType.EVM: return await FolksEvmLoan.prepare.withdraw( FolksCore.getProvider(folksChain.folksChainId), - getSignerAddress(FolksCore.getSigner()), + convertFromGenericAddress(userAddress, folksChain.chainType), messageToSend, folksChain.network, accountId,