Skip to content

Commit

Permalink
feat: reverse extra args completion (#158)
Browse files Browse the repository at this point in the history
Resolve #140 
- Added completion of reverse `extraArgs` when missing
- Added check on reversible actions
- Updated `SendTokenMessageData` to not contain `folksTokenId`
- Added utils to decode `payload` and `data`
  • Loading branch information
palace22 authored Sep 20, 2024
1 parent 988379a commit deef6ce
Show file tree
Hide file tree
Showing 14 changed files with 583 additions and 59 deletions.
5 changes: 5 additions & 0 deletions .changeset/empty-scissors-warn.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@folks-finance/xchain-sdk": patch
---

Added retry and reverse msg value estimation considering bridge router balance
5 changes: 5 additions & 0 deletions .changeset/quiet-timers-applaud.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@folks-finance/xchain-sdk": patch
---

Added reverse extra args completion with gas limit estimation
5 changes: 5 additions & 0 deletions .changeset/thirty-ties-cheat.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@folks-finance/xchain-sdk": patch
---

Updated SendTokenMessageData to not have the extraneous value folksTokenId
5 changes: 5 additions & 0 deletions .changeset/violet-starfishes-stare.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@folks-finance/xchain-sdk": patch
---

Added retry extra args completion with gas limit estimation
4 changes: 4 additions & 0 deletions src/chains/evm/common/types/gmp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,16 @@ export type RetryMessageExtraArgs = {
returnGasLimit: bigint;
};

export type RetryMessageExtraArgsParams = Partial<Omit<RetryMessageExtraArgs, "returnGasLimit">> | undefined;

export type ReverseMessageExtraArgs = {
accountId: AccountId;
returnAdapterId: AdapterType;
returnGasLimit: bigint;
};

export type ReverseMessageExtraArgsParams = Partial<Omit<ReverseMessageExtraArgs, "returnGasLimit">> | undefined;

export type MessageReceived = {
messageId: Hex;
sourceChainId: FolksChainId;
Expand Down
10 changes: 3 additions & 7 deletions src/chains/evm/common/utils/message.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ import type {
OptionalFeeParams,
SendTokenExtraArgs,
} from "../../../../common/types/message.js";
import type { FolksTokenId, FolksSpokeTokenType, FolksHubTokenType } from "../../../../common/types/token.js";
import type { FolksHubTokenType, FolksSpokeTokenType } from "../../../../common/types/token.js";
import type { CCIPAny2EvmMessage } from "../types/gmp.js";
import type { Client, Hex, StateOverride } from "viem";

Expand Down Expand Up @@ -470,12 +470,8 @@ export async function estimateEvmCcipDataGasLimit(
});
}

export function getSendTokenStateOverride(
folksChainId: FolksChainId,
folksTokenId: FolksTokenId,
extraArgs: SendTokenExtraArgs,
) {
const { amount, recipient, token } = extraArgs;
export function getSendTokenStateOverride(folksChainId: FolksChainId, extraArgs: SendTokenExtraArgs) {
const { folksTokenId, amount, recipient, token } = extraArgs;
if (token.type === TokenType.CIRCLE || token.type === TokenType.ERC20) {
const erc20Address = convertFromGenericAddress(token.address, ChainType.EVM);
return getBalanceOfStateOverride([
Expand Down
170 changes: 170 additions & 0 deletions src/chains/evm/hub/utils/message.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
import { RECEIVE_TOKEN_ACTIONS } from "../../../../common/constants/message.js";
import { ChainType } from "../../../../common/types/chain.js";
import { MessageDirection } from "../../../../common/types/gmp.js";
import { Action, AdapterType } from "../../../../common/types/message.js";
import { getSpokeChain, getSpokeTokenData } from "../../../../common/utils/chain.js";
import {
buildMessageToSend,
decodeMessagePayloadData,
estimateAdapterReceiveGasLimit,
} from "../../../../common/utils/messages.js";
import { getFolksTokenIdFromPool } from "../../../../common/utils/token.js";
import { FolksCore } from "../../../../xchain/core/folks-core.js";

import { getBridgeRouterHubContract } from "./contract.js";

import type { GenericAddress } from "../../../../common/types/address.js";
import type { NetworkType } from "../../../../common/types/chain.js";
import type {
MessageBuilderParams,
Payload,
ReceiveTokenAction,
ReversibleHubAction,
SendTokenExtraArgs,
SendTokenMessageData,
} from "../../../../common/types/message.js";
import type {
MessageReceived,
RetryMessageExtraArgs,
RetryMessageExtraArgsParams,
ReverseMessageExtraArgs,
ReverseMessageExtraArgsParams,
} from "../../common/types/gmp.js";
import type { HubChain } from "../types/chain.js";
import type { Client as EVMProvider } from "viem";

export async function getHubRetryMessageExtraArgsAndAdapterFees(
provider: EVMProvider,
hubChain: HubChain,
network: NetworkType,
userAddress: GenericAddress,
message: MessageReceived,
extraArgsParams: RetryMessageExtraArgsParams,
payload: Payload,
): Promise<{
adapterFees: bigint;
extraArgs: RetryMessageExtraArgs;
}> {
const returnAdapterId = extraArgsParams?.returnAdapterId ?? message.returnAdapterId;
const { accountId, action, data } = payload;

// @ts-expect-error: ts(2345)
if (!RECEIVE_TOKEN_ACTIONS.includes(action))
return { adapterFees: 0n, extraArgs: { returnAdapterId, returnGasLimit: 0n } };
const payloadData = decodeMessagePayloadData(action as ReceiveTokenAction, data);

const folksTokenId = getFolksTokenIdFromPool(payloadData.poolId);

const spokeChain = getSpokeChain(payloadData.receiverFolksChainId, network);
const spokeTokenData = getSpokeTokenData(spokeChain, folksTokenId);

const returnData: SendTokenMessageData = {
amount: payloadData.amount,
};
const returnExtraArgs: SendTokenExtraArgs = {
folksTokenId,
token: spokeTokenData.token,
recipient: spokeTokenData.spokeAddress,
amount: payloadData.amount,
};
const returnMessageBuilderParams: MessageBuilderParams = {
userAddress,
accountId,
adapters: {
adapterId: AdapterType.HUB,
returnAdapterId: extraArgsParams?.returnAdapterId ?? message.returnAdapterId,
},
action: Action.SendToken,
sender: hubChain.hubAddress,
destinationChainId: payloadData.receiverFolksChainId,
handler: spokeTokenData.spokeAddress,
data: returnData,
extraArgs: returnExtraArgs,
};
const returnGasLimit = await estimateAdapterReceiveGasLimit(
hubChain.folksChainId,
payloadData.receiverFolksChainId,
FolksCore.getEVMProvider(payloadData.receiverFolksChainId),
network,
MessageDirection.HubToSpoke,
returnMessageBuilderParams,
);

const bridgeRouter = getBridgeRouterHubContract(provider, hubChain.bridgeRouterAddress);
const messageToSend = buildMessageToSend(ChainType.EVM, returnMessageBuilderParams);
const adapterFees = await bridgeRouter.read.getSendFee([messageToSend]);

return {
adapterFees,
extraArgs: { returnAdapterId, returnGasLimit },
};
}

export async function getHubReverseMessageExtraArgsAndAdapterFees(
provider: EVMProvider,
hubChain: HubChain,
network: NetworkType,
userAddress: GenericAddress,
message: MessageReceived,
extraArgsParams: ReverseMessageExtraArgsParams,
payload: Payload,
): Promise<{
adapterFees: bigint;
extraArgs: ReverseMessageExtraArgs;
}> {
const { action, data } = payload;
const payloadData = decodeMessagePayloadData(action as ReversibleHubAction, data);

const returnAdapterId = extraArgsParams?.returnAdapterId ?? message.returnAdapterId;
const accountId = extraArgsParams?.accountId ?? payload.accountId;

const folksTokenId = getFolksTokenIdFromPool(payloadData.poolId);

const spokeChain = getSpokeChain(message.sourceChainId, network);
const spokeTokenData = getSpokeTokenData(spokeChain, folksTokenId);

const returnData: SendTokenMessageData = {
amount: payloadData.amount,
};
const returnExtraArgs: SendTokenExtraArgs = {
folksTokenId,
token: spokeTokenData.token,
recipient: spokeTokenData.spokeAddress,
amount: payloadData.amount,
};
const returnMessageBuilderParams: MessageBuilderParams = {
userAddress,
accountId,
adapters: {
adapterId: AdapterType.HUB,
returnAdapterId,
},
action: Action.SendToken,
sender: hubChain.hubAddress,
destinationChainId: message.sourceChainId,
handler: spokeTokenData.spokeAddress,
data: returnData,
extraArgs: returnExtraArgs,
};
const returnGasLimit = await estimateAdapterReceiveGasLimit(
hubChain.folksChainId,
message.sourceChainId,
FolksCore.getEVMProvider(message.sourceChainId),
network,
MessageDirection.HubToSpoke,
returnMessageBuilderParams,
);

const bridgeRouter = getBridgeRouterHubContract(provider, hubChain.bridgeRouterAddress);
const messageToSend = buildMessageToSend(ChainType.EVM, returnMessageBuilderParams);
const adapterFees = await bridgeRouter.read.getSendFee([messageToSend]);

return {
adapterFees,
extraArgs: {
accountId,
returnAdapterId,
returnGasLimit,
},
};
}
8 changes: 8 additions & 0 deletions src/common/constants/message.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,12 @@
import { Action } from "../types/message.js";

export const FINALITY = {
IMMEDIATE: BigInt(0),
FINALISED: BigInt(1),
} as const;

export const REVERSIBLE_HUB_ACTIONS = [Action.CreateLoanAndDeposit, Action.Deposit, Action.Repay] as const;

export const SEND_TOKEN_ACTIONS = [Action.CreateLoanAndDeposit, Action.Deposit, Action.Repay] as const;
export const RECEIVE_TOKEN_ACTIONS = [Action.Withdraw, Action.Borrow] as const;
export const HUB_ACTIONS = [Action.DepositFToken, Action.WithdrawFToken, Action.Liquidate, Action.SendToken] as const;
4 changes: 4 additions & 0 deletions src/common/constants/pool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,7 @@ export const TESTNET_POOLS = {
[TESTNET_FOLKS_TOKEN_ID.LINK_eth_sep]: 133,
[TESTNET_FOLKS_TOKEN_ID.BNB]: 134,
} as const satisfies Partial<Record<FolksTokenId, number>>;

export const FOLKS_TOKEN_IDS_FROM_POOL = Object.fromEntries(
Object.entries(TESTNET_POOLS).map(([token, poolId]) => [poolId, token]),
) as Partial<Record<number, FolksTokenId>>;
51 changes: 43 additions & 8 deletions src/common/types/message.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,13 @@ import type { FolksChainId } from "./chain.js";
import type { AccountId, LoanId, LoanName, Nonce } from "./lending.js";
import type { LoanTypeId } from "./module.js";
import type { FolksTokenId, FolksSpokeTokenType } from "./token.js";
import type { FINALITY } from "../constants/message.js";
import type {
FINALITY,
HUB_ACTIONS,
RECEIVE_TOKEN_ACTIONS,
REVERSIBLE_HUB_ACTIONS,
SEND_TOKEN_ACTIONS,
} from "../constants/message.js";
import type { Hex } from "viem";

export enum AdapterType {
Expand Down Expand Up @@ -38,12 +44,12 @@ export enum Action {
SendToken,
}

export type SendTokenAction = Extract<Action, Action.CreateLoanAndDeposit | Action.Deposit | Action.Repay>;
export type ReceiveTokenAction = Extract<Action, Action.Withdraw | Action.Borrow>;
export type HubAction = Extract<
Action,
Action.DepositFToken | Action.WithdrawFToken | Action.Liquidate | Action.SendToken
>;
export type SendTokenAction = Extract<Action, (typeof SEND_TOKEN_ACTIONS)[number]>;
export type ReceiveTokenAction = Extract<Action, (typeof RECEIVE_TOKEN_ACTIONS)[number]>;
export type HubAction = Extract<Action, (typeof HUB_ACTIONS)[number]>;

export type ReversibleHubAction = Extract<Action, (typeof REVERSIBLE_HUB_ACTIONS)[number]>;

export type DataAction = Extract<
Action,
| Action.CreateAccount
Expand Down Expand Up @@ -80,6 +86,13 @@ export type MessageParams = FeeParams & MessageAdapters;

export type OptionalFeeParams = Partial<FeeParams>;

export type Payload = {
action: Action;
accountId: AccountId;
userAddr: GenericAddress;
data: Hex;
};

export type MessageToSend = {
params: MessageParams;
sender: GenericAddress;
Expand Down Expand Up @@ -180,7 +193,6 @@ export type LiquidateMessageData = {
};

export type SendTokenMessageData = {
folksTokenId: FolksTokenId;
amount: bigint;
};

Expand All @@ -201,11 +213,34 @@ export type RepayExtraArgs = {
};

export type SendTokenExtraArgs = {
folksTokenId: FolksTokenId;
token: FolksSpokeTokenType;
recipient: GenericAddress;
amount: bigint;
};

export type MessageDataMap = {
[Action.AcceptInviteAddress]: DefaultMessageData;
[Action.AddDelegate]: DefaultMessageData;
[Action.RemoveDelegate]: DefaultMessageData;
[Action.DepositFToken]: DefaultMessageData;
[Action.WithdrawFToken]: DefaultMessageData;
[Action.CreateAccount]: CreateAccountMessageData;
[Action.InviteAddress]: InviteAddressMessageData;
[Action.UnregisterAddress]: UnregisterAddressMessageData;
[Action.CreateLoan]: CreateLoanMessageData;
[Action.DeleteLoan]: DeleteLoanMessageData;
[Action.CreateLoanAndDeposit]: CreateLoanAndDepositMessageData;
[Action.Deposit]: DepositMessageData;
[Action.Withdraw]: WithdrawMessageData;
[Action.Borrow]: BorrowMessageData;
[Action.Repay]: RepayMessageData;
[Action.RepayWithCollateral]: RepayWithCollateralMessageData;
[Action.SwitchBorrowType]: SwitchBorrowTypeMessageData;
[Action.Liquidate]: LiquidateMessageData;
[Action.SendToken]: SendTokenMessageData;
};

// Params
export type DefaultMessageDataParams = {
action:
Expand Down
Loading

0 comments on commit deef6ce

Please sign in to comment.