@across-protocol/sdk 4.3.12 → 4.3.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/arch/svm/BlockUtils.d.ts +1 -0
- package/dist/cjs/arch/svm/BlockUtils.js +69 -52
- package/dist/cjs/arch/svm/BlockUtils.js.map +1 -1
- package/dist/cjs/arch/svm/SpokeUtils.d.ts +21 -10
- package/dist/cjs/arch/svm/SpokeUtils.js +303 -49
- package/dist/cjs/arch/svm/SpokeUtils.js.map +1 -1
- package/dist/cjs/arch/svm/types.d.ts +7 -0
- package/dist/cjs/arch/svm/utils.d.ts +8 -3
- package/dist/cjs/arch/svm/utils.js +81 -4
- package/dist/cjs/arch/svm/utils.js.map +1 -1
- package/dist/cjs/clients/BundleDataClient/utils/PoolRebalanceUtils.d.ts +1 -1
- package/dist/cjs/clients/BundleDataClient/utils/PoolRebalanceUtils.js +5 -2
- package/dist/cjs/clients/BundleDataClient/utils/PoolRebalanceUtils.js.map +1 -1
- package/dist/cjs/clients/BundleDataClient/utils/SuperstructUtils.d.ts +8 -8
- package/dist/cjs/clients/HubPoolClient.d.ts +1 -0
- package/dist/cjs/clients/HubPoolClient.js +11 -0
- package/dist/cjs/clients/HubPoolClient.js.map +1 -1
- package/dist/esm/arch/svm/BlockUtils.d.ts +7 -0
- package/dist/esm/arch/svm/BlockUtils.js +76 -54
- package/dist/esm/arch/svm/BlockUtils.js.map +1 -1
- package/dist/esm/arch/svm/SpokeUtils.d.ts +51 -10
- package/dist/esm/arch/svm/SpokeUtils.js +294 -8
- package/dist/esm/arch/svm/SpokeUtils.js.map +1 -1
- package/dist/esm/arch/svm/types.d.ts +7 -0
- package/dist/esm/arch/svm/utils.d.ts +35 -3
- package/dist/esm/arch/svm/utils.js +103 -3
- package/dist/esm/arch/svm/utils.js.map +1 -1
- package/dist/esm/clients/BundleDataClient/utils/PoolRebalanceUtils.d.ts +1 -1
- package/dist/esm/clients/BundleDataClient/utils/PoolRebalanceUtils.js +5 -2
- package/dist/esm/clients/BundleDataClient/utils/PoolRebalanceUtils.js.map +1 -1
- package/dist/esm/clients/BundleDataClient/utils/SuperstructUtils.d.ts +8 -8
- package/dist/esm/clients/HubPoolClient.d.ts +1 -0
- package/dist/esm/clients/HubPoolClient.js +19 -0
- package/dist/esm/clients/HubPoolClient.js.map +1 -1
- package/dist/types/arch/svm/BlockUtils.d.ts +7 -0
- package/dist/types/arch/svm/BlockUtils.d.ts.map +1 -1
- package/dist/types/arch/svm/SpokeUtils.d.ts +51 -10
- package/dist/types/arch/svm/SpokeUtils.d.ts.map +1 -1
- package/dist/types/arch/svm/types.d.ts +7 -0
- package/dist/types/arch/svm/types.d.ts.map +1 -1
- package/dist/types/arch/svm/utils.d.ts +35 -3
- package/dist/types/arch/svm/utils.d.ts.map +1 -1
- package/dist/types/clients/BundleDataClient/utils/PoolRebalanceUtils.d.ts +1 -1
- package/dist/types/clients/BundleDataClient/utils/PoolRebalanceUtils.d.ts.map +1 -1
- package/dist/types/clients/BundleDataClient/utils/SuperstructUtils.d.ts +8 -8
- package/dist/types/clients/HubPoolClient.d.ts +1 -0
- package/dist/types/clients/HubPoolClient.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/arch/svm/BlockUtils.ts +33 -16
- package/src/arch/svm/SpokeUtils.ts +272 -10
- package/src/arch/svm/types.ts +8 -0
- package/src/arch/svm/utils.ts +99 -8
- package/src/clients/BundleDataClient/utils/PoolRebalanceUtils.ts +5 -2
- package/src/clients/HubPoolClient.ts +23 -0
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { SvmSpokeClient } from "@across-protocol/contracts";
|
|
1
|
+
import { MessageTransmitterClient, SvmSpokeClient, TokenMessengerMinterClient } from "@across-protocol/contracts";
|
|
2
2
|
import { decodeFillStatusAccount, fetchState } from "@across-protocol/contracts/dist/src/svm/clients/SvmSpoke";
|
|
3
|
-
import { intToU8Array32 } from "@across-protocol/contracts/dist/src/svm/web3-v1/conversionUtils";
|
|
4
3
|
import { hashNonEmptyMessage } from "@across-protocol/contracts/dist/src/svm/web3-v1";
|
|
4
|
+
import { intToU8Array32 } from "@across-protocol/contracts/dist/src/svm/web3-v1/conversionUtils";
|
|
5
|
+
import { SYSTEM_PROGRAM_ADDRESS } from "@solana-program/system";
|
|
5
6
|
import {
|
|
6
7
|
ASSOCIATED_TOKEN_PROGRAM_ADDRESS,
|
|
7
8
|
TOKEN_PROGRAM_ADDRESS,
|
|
@@ -10,48 +11,60 @@ import {
|
|
|
10
11
|
getCreateAssociatedTokenIdempotentInstruction,
|
|
11
12
|
} from "@solana-program/token";
|
|
12
13
|
import {
|
|
14
|
+
AccountRole,
|
|
13
15
|
Address,
|
|
16
|
+
IAccountMeta,
|
|
17
|
+
KeyPairSigner,
|
|
18
|
+
ReadonlyUint8Array,
|
|
14
19
|
appendTransactionMessageInstruction,
|
|
15
20
|
fetchEncodedAccount,
|
|
16
21
|
fetchEncodedAccounts,
|
|
17
22
|
getAddressEncoder,
|
|
23
|
+
getBase64EncodedWireTransaction,
|
|
18
24
|
getProgramDerivedAddress,
|
|
25
|
+
getSignatureFromTransaction,
|
|
19
26
|
getU32Encoder,
|
|
20
27
|
getU64Encoder,
|
|
21
28
|
pipe,
|
|
22
|
-
|
|
29
|
+
signTransactionMessageWithSigners,
|
|
23
30
|
some,
|
|
24
31
|
type TransactionSigner,
|
|
25
32
|
} from "@solana/kit";
|
|
26
33
|
import assert from "assert";
|
|
27
34
|
import { arrayify, hexZeroPad, hexlify } from "ethers/lib/utils";
|
|
28
35
|
import { Logger } from "winston";
|
|
29
|
-
import {
|
|
36
|
+
import { CHAIN_IDs, TOKEN_SYMBOLS_MAP } from "../../constants";
|
|
30
37
|
import { DepositWithBlock, FillStatus, FillWithBlock, RelayData, RelayExecutionEventInfo } from "../../interfaces";
|
|
31
38
|
import {
|
|
32
39
|
BigNumber,
|
|
33
40
|
EvmAddress,
|
|
34
|
-
SvmAddress,
|
|
35
41
|
Address as SdkAddress,
|
|
42
|
+
SvmAddress,
|
|
43
|
+
bs58,
|
|
44
|
+
chainIsProd,
|
|
36
45
|
chainIsSvm,
|
|
37
46
|
chunk,
|
|
38
47
|
isUnsafeDepositId,
|
|
39
48
|
keccak256,
|
|
49
|
+
mapAsync,
|
|
40
50
|
toAddressType,
|
|
41
51
|
} from "../../utils";
|
|
42
|
-
import { SvmCpiEventsClient } from "./eventsClient";
|
|
43
52
|
import {
|
|
44
53
|
bigToU8a32,
|
|
45
54
|
createDefaultTransaction,
|
|
55
|
+
getCCTPNoncePda,
|
|
46
56
|
getEventAuthority,
|
|
47
57
|
getFillStatusPda,
|
|
58
|
+
getSelfAuthority,
|
|
48
59
|
getStatePda,
|
|
60
|
+
isDepositForBurnEvent,
|
|
61
|
+
simulateAndDecode,
|
|
49
62
|
toAddress,
|
|
50
63
|
unwrapEventData,
|
|
51
|
-
} from "./
|
|
52
|
-
import {
|
|
53
|
-
import {
|
|
54
|
-
import { SVMEventNames, SVMProvider } from "./types";
|
|
64
|
+
} from "./";
|
|
65
|
+
import { SvmCpiEventsClient } from "./eventsClient";
|
|
66
|
+
import { SVM_NO_BLOCK_AT_SLOT, isSolanaError } from "./provider";
|
|
67
|
+
import { AttestedCCTPMessage, SVMEventNames, SVMProvider } from "./types";
|
|
55
68
|
|
|
56
69
|
/**
|
|
57
70
|
* @note: Average Solana slot duration is about 400-500ms. We can be conservative
|
|
@@ -796,6 +809,19 @@ export const createCloseFillPdaInstruction = async (
|
|
|
796
809
|
);
|
|
797
810
|
};
|
|
798
811
|
|
|
812
|
+
export const createReceiveMessageInstruction = async (
|
|
813
|
+
signer: TransactionSigner,
|
|
814
|
+
solanaClient: SVMProvider,
|
|
815
|
+
input: MessageTransmitterClient.ReceiveMessageInput,
|
|
816
|
+
remainingAccounts: IAccountMeta<string>[]
|
|
817
|
+
) => {
|
|
818
|
+
const receiveMessageIx = await MessageTransmitterClient.getReceiveMessageInstruction(input);
|
|
819
|
+
(receiveMessageIx.accounts as IAccountMeta<string>[]).push(...remainingAccounts);
|
|
820
|
+
return pipe(await createDefaultTransaction(solanaClient, signer), (tx) =>
|
|
821
|
+
appendTransactionMessageInstruction(receiveMessageIx, tx)
|
|
822
|
+
);
|
|
823
|
+
};
|
|
824
|
+
|
|
799
825
|
export async function getAssociatedTokenAddress(
|
|
800
826
|
owner: SvmAddress,
|
|
801
827
|
mint: SvmAddress,
|
|
@@ -1048,3 +1074,239 @@ export async function getFillRelayDelegatePda(
|
|
|
1048
1074
|
|
|
1049
1075
|
return pda;
|
|
1050
1076
|
}
|
|
1077
|
+
|
|
1078
|
+
/**
|
|
1079
|
+
* Checks if a CCTP message has been processed.
|
|
1080
|
+
* @param solanaClient The Solana client.
|
|
1081
|
+
* @param signer The signer of the transaction.
|
|
1082
|
+
* @param nonce The nonce to check.
|
|
1083
|
+
* @param sourceDomain The source domain.
|
|
1084
|
+
* @returns True if the message has been processed, false otherwise.
|
|
1085
|
+
*/
|
|
1086
|
+
export const hasCCTPV1MessageBeenProcessed = async (
|
|
1087
|
+
solanaClient: SVMProvider,
|
|
1088
|
+
signer: KeyPairSigner,
|
|
1089
|
+
nonce: number,
|
|
1090
|
+
sourceDomain: number
|
|
1091
|
+
): Promise<boolean> => {
|
|
1092
|
+
const noncePda = await getCCTPNoncePda(solanaClient, signer, nonce, sourceDomain);
|
|
1093
|
+
const isNonceUsedIx = await MessageTransmitterClient.getIsNonceUsedInstruction({
|
|
1094
|
+
nonce: nonce,
|
|
1095
|
+
usedNonces: noncePda,
|
|
1096
|
+
});
|
|
1097
|
+
const parserFunction = (buf: Buffer): boolean => {
|
|
1098
|
+
if (buf.length != 1) {
|
|
1099
|
+
throw new Error("Invalid buffer length for isNonceUsedIx");
|
|
1100
|
+
}
|
|
1101
|
+
return Boolean(buf[0]);
|
|
1102
|
+
};
|
|
1103
|
+
return await simulateAndDecode(solanaClient, isNonceUsedIx, signer, parserFunction);
|
|
1104
|
+
};
|
|
1105
|
+
|
|
1106
|
+
/**
|
|
1107
|
+
* Returns the account metas for a tokenless message.
|
|
1108
|
+
* @returns The account metas for a tokenless message.
|
|
1109
|
+
*/
|
|
1110
|
+
export async function getAccountMetasForTokenlessMessage(): Promise<IAccountMeta<string>[]> {
|
|
1111
|
+
const statePda = await getStatePda(SvmSpokeClient.SVM_SPOKE_PROGRAM_ADDRESS);
|
|
1112
|
+
return [
|
|
1113
|
+
{ address: statePda, role: AccountRole.READONLY },
|
|
1114
|
+
{ address: await getSelfAuthority(), role: AccountRole.READONLY },
|
|
1115
|
+
{ address: SvmSpokeClient.SVM_SPOKE_PROGRAM_ADDRESS, role: AccountRole.READONLY },
|
|
1116
|
+
{ address: statePda, role: AccountRole.WRITABLE },
|
|
1117
|
+
{ address: await getEventAuthority(SvmSpokeClient.SVM_SPOKE_PROGRAM_ADDRESS), role: AccountRole.READONLY },
|
|
1118
|
+
{ address: SvmSpokeClient.SVM_SPOKE_PROGRAM_ADDRESS, role: AccountRole.READONLY },
|
|
1119
|
+
];
|
|
1120
|
+
}
|
|
1121
|
+
|
|
1122
|
+
/**
|
|
1123
|
+
* Returns the account metas for a deposit message.
|
|
1124
|
+
* @param message The CCTP message.
|
|
1125
|
+
* @param hubChainId The chain ID of the hub.
|
|
1126
|
+
* @param tokenMessengerMinter The token messenger minter address.
|
|
1127
|
+
* @returns The account metas for a deposit message.
|
|
1128
|
+
*/
|
|
1129
|
+
async function getAccountMetasForDepositMessage(
|
|
1130
|
+
message: AttestedCCTPMessage,
|
|
1131
|
+
hubChainId: number,
|
|
1132
|
+
tokenMessengerMinter: Address
|
|
1133
|
+
): Promise<IAccountMeta<string>[]> {
|
|
1134
|
+
const l1Usdc = EvmAddress.from(TOKEN_SYMBOLS_MAP.USDC.addresses[hubChainId]);
|
|
1135
|
+
const l2Usdc = SvmAddress.from(
|
|
1136
|
+
TOKEN_SYMBOLS_MAP.USDC.addresses[chainIsProd(hubChainId) ? CHAIN_IDs.SOLANA : CHAIN_IDs.SOLANA_DEVNET]
|
|
1137
|
+
);
|
|
1138
|
+
|
|
1139
|
+
const [tokenMessengerPda] = await getProgramDerivedAddress({
|
|
1140
|
+
programAddress: tokenMessengerMinter,
|
|
1141
|
+
seeds: ["token_messenger"],
|
|
1142
|
+
});
|
|
1143
|
+
|
|
1144
|
+
const [tokenMinterPda] = await getProgramDerivedAddress({
|
|
1145
|
+
programAddress: tokenMessengerMinter,
|
|
1146
|
+
seeds: ["token_minter"],
|
|
1147
|
+
});
|
|
1148
|
+
|
|
1149
|
+
const [localTokenPda] = await getProgramDerivedAddress({
|
|
1150
|
+
programAddress: tokenMessengerMinter,
|
|
1151
|
+
seeds: ["local_token", bs58.decode(l2Usdc.toBase58())],
|
|
1152
|
+
});
|
|
1153
|
+
|
|
1154
|
+
const [tokenMessengerEventAuthorityPda] = await getProgramDerivedAddress({
|
|
1155
|
+
programAddress: tokenMessengerMinter,
|
|
1156
|
+
seeds: ["__event_authority"],
|
|
1157
|
+
});
|
|
1158
|
+
|
|
1159
|
+
const [custodyTokenAccountPda] = await getProgramDerivedAddress({
|
|
1160
|
+
programAddress: tokenMessengerMinter,
|
|
1161
|
+
seeds: ["custody", bs58.decode(l2Usdc.toBase58())],
|
|
1162
|
+
});
|
|
1163
|
+
|
|
1164
|
+
const state = await getStatePda(SvmSpokeClient.SVM_SPOKE_PROGRAM_ADDRESS);
|
|
1165
|
+
const tokenProgram = TOKEN_PROGRAM_ADDRESS;
|
|
1166
|
+
const vault = await getAssociatedTokenAddress(
|
|
1167
|
+
SvmAddress.from(state),
|
|
1168
|
+
SvmAddress.from(l2Usdc.toBase58()),
|
|
1169
|
+
tokenProgram
|
|
1170
|
+
);
|
|
1171
|
+
|
|
1172
|
+
// Define accounts dependent on deposit information.
|
|
1173
|
+
const [tokenPairPda] = await getProgramDerivedAddress({
|
|
1174
|
+
programAddress: tokenMessengerMinter,
|
|
1175
|
+
seeds: [
|
|
1176
|
+
new Uint8Array(Buffer.from("token_pair")),
|
|
1177
|
+
new Uint8Array(Buffer.from(String(message.sourceDomain))),
|
|
1178
|
+
new Uint8Array(Buffer.from(l1Usdc.toBytes32().slice(2), "hex")),
|
|
1179
|
+
],
|
|
1180
|
+
});
|
|
1181
|
+
|
|
1182
|
+
const [remoteTokenMessengerPda] = await getProgramDerivedAddress({
|
|
1183
|
+
programAddress: tokenMessengerMinter,
|
|
1184
|
+
seeds: ["remote_token_messenger", String(message.sourceDomain)],
|
|
1185
|
+
});
|
|
1186
|
+
|
|
1187
|
+
return [
|
|
1188
|
+
{ address: tokenMessengerPda, role: AccountRole.READONLY },
|
|
1189
|
+
{ address: remoteTokenMessengerPda, role: AccountRole.READONLY },
|
|
1190
|
+
{ address: tokenMinterPda, role: AccountRole.WRITABLE },
|
|
1191
|
+
{ address: localTokenPda, role: AccountRole.WRITABLE },
|
|
1192
|
+
{ address: tokenPairPda, role: AccountRole.READONLY },
|
|
1193
|
+
{ address: vault, role: AccountRole.WRITABLE },
|
|
1194
|
+
{ address: custodyTokenAccountPda, role: AccountRole.WRITABLE },
|
|
1195
|
+
{ address: TOKEN_PROGRAM_ADDRESS, role: AccountRole.READONLY },
|
|
1196
|
+
{ address: tokenMessengerEventAuthorityPda, role: AccountRole.READONLY },
|
|
1197
|
+
{ address: tokenMessengerMinter, role: AccountRole.READONLY },
|
|
1198
|
+
];
|
|
1199
|
+
}
|
|
1200
|
+
|
|
1201
|
+
/**
|
|
1202
|
+
* Returns the CCTP v1 receive message transaction.
|
|
1203
|
+
* @param solanaClient The Solana client.
|
|
1204
|
+
* @param signer The signer of the transaction.
|
|
1205
|
+
* @param message The CCTP message.
|
|
1206
|
+
* @param hubChainId The chain ID of the hub.
|
|
1207
|
+
* @returns The CCTP v1 receive message transaction.
|
|
1208
|
+
*/
|
|
1209
|
+
export async function getCCTPV1ReceiveMessageTx(
|
|
1210
|
+
solanaClient: SVMProvider,
|
|
1211
|
+
signer: KeyPairSigner,
|
|
1212
|
+
message: AttestedCCTPMessage,
|
|
1213
|
+
hubChainId: number
|
|
1214
|
+
) {
|
|
1215
|
+
const [messageTransmitterPda] = await getProgramDerivedAddress({
|
|
1216
|
+
programAddress: MessageTransmitterClient.MESSAGE_TRANSMITTER_PROGRAM_ADDRESS,
|
|
1217
|
+
seeds: ["message_transmitter"],
|
|
1218
|
+
});
|
|
1219
|
+
|
|
1220
|
+
const [eventAuthorityPda] = await getProgramDerivedAddress({
|
|
1221
|
+
programAddress: MessageTransmitterClient.MESSAGE_TRANSMITTER_PROGRAM_ADDRESS,
|
|
1222
|
+
seeds: ["__event_authority"],
|
|
1223
|
+
});
|
|
1224
|
+
|
|
1225
|
+
const cctpMessageReceiver = isDepositForBurnEvent(message)
|
|
1226
|
+
? TokenMessengerMinterClient.TOKEN_MESSENGER_MINTER_PROGRAM_ADDRESS
|
|
1227
|
+
: SvmSpokeClient.SVM_SPOKE_PROGRAM_ADDRESS;
|
|
1228
|
+
|
|
1229
|
+
const [authorityPda] = await getProgramDerivedAddress({
|
|
1230
|
+
programAddress: MessageTransmitterClient.MESSAGE_TRANSMITTER_PROGRAM_ADDRESS,
|
|
1231
|
+
seeds: ["message_transmitter_authority", bs58.decode(cctpMessageReceiver)],
|
|
1232
|
+
});
|
|
1233
|
+
|
|
1234
|
+
// Notice: message.nonce is only valid for v1 messages
|
|
1235
|
+
const usedNonces = await getCCTPNoncePda(solanaClient, signer, message.nonce, message.sourceDomain);
|
|
1236
|
+
|
|
1237
|
+
// Notice: for Svm tokenless messages, we currently only support very specific finalizations: Hub -> Spoke relayRootBundle calls
|
|
1238
|
+
const accountMetas: IAccountMeta<string>[] = isDepositForBurnEvent(message)
|
|
1239
|
+
? await getAccountMetasForDepositMessage(
|
|
1240
|
+
message,
|
|
1241
|
+
hubChainId,
|
|
1242
|
+
TokenMessengerMinterClient.TOKEN_MESSENGER_MINTER_PROGRAM_ADDRESS
|
|
1243
|
+
)
|
|
1244
|
+
: await getAccountMetasForTokenlessMessage();
|
|
1245
|
+
|
|
1246
|
+
const messageBytes = message.messageBytes.startsWith("0x")
|
|
1247
|
+
? Buffer.from(message.messageBytes.slice(2), "hex")
|
|
1248
|
+
: Buffer.from(message.messageBytes, "hex");
|
|
1249
|
+
|
|
1250
|
+
const input: MessageTransmitterClient.ReceiveMessageInput = {
|
|
1251
|
+
program: MessageTransmitterClient.MESSAGE_TRANSMITTER_PROGRAM_ADDRESS,
|
|
1252
|
+
payer: signer,
|
|
1253
|
+
caller: signer,
|
|
1254
|
+
authorityPda,
|
|
1255
|
+
messageTransmitter: messageTransmitterPda,
|
|
1256
|
+
eventAuthority: eventAuthorityPda,
|
|
1257
|
+
usedNonces,
|
|
1258
|
+
receiver: SvmSpokeClient.SVM_SPOKE_PROGRAM_ADDRESS,
|
|
1259
|
+
systemProgram: SYSTEM_PROGRAM_ADDRESS,
|
|
1260
|
+
message: messageBytes,
|
|
1261
|
+
attestation: Buffer.from(message.attestation.slice(2), "hex"),
|
|
1262
|
+
};
|
|
1263
|
+
|
|
1264
|
+
return createReceiveMessageInstruction(signer, solanaClient, input, accountMetas);
|
|
1265
|
+
}
|
|
1266
|
+
|
|
1267
|
+
/**
|
|
1268
|
+
* Finalizes CCTP deposits and messages on Solana.
|
|
1269
|
+
*
|
|
1270
|
+
* @param solanaClient The Solana client.
|
|
1271
|
+
* @param attestedMessages The CCTP messages to Solana.
|
|
1272
|
+
* @param signer A base signer to be converted into a Solana signer.
|
|
1273
|
+
* @param simulate Whether to simulate the transaction.
|
|
1274
|
+
* @param hubChainId The chain ID of the hub.
|
|
1275
|
+
* @returns A list of executed transaction signatures.
|
|
1276
|
+
*/
|
|
1277
|
+
|
|
1278
|
+
export function finalizeCCTPV1Messages(
|
|
1279
|
+
solanaClient: SVMProvider,
|
|
1280
|
+
attestedMessages: AttestedCCTPMessage[],
|
|
1281
|
+
signer: KeyPairSigner,
|
|
1282
|
+
simulate = false,
|
|
1283
|
+
hubChainId = 1
|
|
1284
|
+
): Promise<string[]> {
|
|
1285
|
+
return mapAsync(attestedMessages, async (message) => {
|
|
1286
|
+
const receiveMessageIx = await getCCTPV1ReceiveMessageTx(solanaClient, signer, message, hubChainId);
|
|
1287
|
+
|
|
1288
|
+
if (simulate) {
|
|
1289
|
+
const result = await solanaClient
|
|
1290
|
+
.simulateTransaction(
|
|
1291
|
+
getBase64EncodedWireTransaction(await signTransactionMessageWithSigners(receiveMessageIx)),
|
|
1292
|
+
{
|
|
1293
|
+
encoding: "base64",
|
|
1294
|
+
}
|
|
1295
|
+
)
|
|
1296
|
+
.send();
|
|
1297
|
+
if (result.value.err) {
|
|
1298
|
+
throw new Error(result.value.err.toString());
|
|
1299
|
+
}
|
|
1300
|
+
return "";
|
|
1301
|
+
}
|
|
1302
|
+
|
|
1303
|
+
const signedTransaction = await signTransactionMessageWithSigners(receiveMessageIx);
|
|
1304
|
+
const signature = getSignatureFromTransaction(signedTransaction);
|
|
1305
|
+
const encodedTransaction = getBase64EncodedWireTransaction(signedTransaction);
|
|
1306
|
+
await solanaClient
|
|
1307
|
+
.sendTransaction(encodedTransaction, { preflightCommitment: "confirmed", encoding: "base64" })
|
|
1308
|
+
.send();
|
|
1309
|
+
|
|
1310
|
+
return signature;
|
|
1311
|
+
});
|
|
1312
|
+
}
|
package/src/arch/svm/types.ts
CHANGED
|
@@ -61,3 +61,11 @@ export type RpcClient = {
|
|
|
61
61
|
rpc: SVMProvider;
|
|
62
62
|
rpcSubscriptions: RpcSubscriptions<SignatureNotificationsApi & SlotNotificationsApi>;
|
|
63
63
|
};
|
|
64
|
+
|
|
65
|
+
export type AttestedCCTPMessage = {
|
|
66
|
+
nonce: number;
|
|
67
|
+
sourceDomain: number;
|
|
68
|
+
messageBytes: string;
|
|
69
|
+
attestation: string;
|
|
70
|
+
type: "transfer" | "message";
|
|
71
|
+
};
|
package/src/arch/svm/utils.ts
CHANGED
|
@@ -1,24 +1,29 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { ethers } from "ethers";
|
|
1
|
+
import { MessageTransmitterClient, SvmSpokeClient } from "@across-protocol/contracts";
|
|
3
2
|
import { BN, BorshEventCoder, Idl } from "@coral-xyz/anchor";
|
|
4
3
|
import {
|
|
5
4
|
Address,
|
|
5
|
+
IInstruction,
|
|
6
|
+
KeyPairSigner,
|
|
6
7
|
address,
|
|
8
|
+
appendTransactionMessageInstruction,
|
|
9
|
+
createTransactionMessage,
|
|
7
10
|
getAddressEncoder,
|
|
11
|
+
getBase64EncodedWireTransaction,
|
|
8
12
|
getProgramDerivedAddress,
|
|
9
|
-
getU64Encoder,
|
|
10
13
|
getU32Encoder,
|
|
14
|
+
getU64Encoder,
|
|
11
15
|
isAddress,
|
|
12
|
-
type TransactionSigner,
|
|
13
16
|
pipe,
|
|
14
|
-
createTransactionMessage,
|
|
15
17
|
setTransactionMessageFeePayerSigner,
|
|
16
18
|
setTransactionMessageLifetimeUsingBlockhash,
|
|
19
|
+
signTransactionMessageWithSigners,
|
|
20
|
+
type TransactionSigner,
|
|
17
21
|
} from "@solana/kit";
|
|
18
|
-
import
|
|
22
|
+
import bs58 from "bs58";
|
|
23
|
+
import { ethers } from "ethers";
|
|
19
24
|
import { FillType, RelayData } from "../../interfaces";
|
|
20
|
-
import { BigNumber, getRelayDataHash, isDefined, isUint8Array
|
|
21
|
-
import { EventName, SVMEventNames, SVMProvider } from "./types";
|
|
25
|
+
import { BigNumber, Address as SdkAddress, getRelayDataHash, isDefined, isUint8Array } from "../../utils";
|
|
26
|
+
import { AttestedCCTPMessage, EventName, SVMEventNames, SVMProvider } from "./types";
|
|
22
27
|
|
|
23
28
|
export { isSolanaError } from "@solana/kit";
|
|
24
29
|
|
|
@@ -334,6 +339,18 @@ export async function getEventAuthority(programId: Address): Promise<Address> {
|
|
|
334
339
|
return eventAuthority;
|
|
335
340
|
}
|
|
336
341
|
|
|
342
|
+
/**
|
|
343
|
+
* Returns the PDA for the Self Authority.
|
|
344
|
+
* @returns The PDA for the Self Authority.
|
|
345
|
+
*/
|
|
346
|
+
export const getSelfAuthority = async () => {
|
|
347
|
+
const [selfAuthority] = await getProgramDerivedAddress({
|
|
348
|
+
programAddress: address(SvmSpokeClient.SVM_SPOKE_PROGRAM_ADDRESS),
|
|
349
|
+
seeds: ["self_authority"],
|
|
350
|
+
});
|
|
351
|
+
return selfAuthority;
|
|
352
|
+
};
|
|
353
|
+
|
|
337
354
|
/**
|
|
338
355
|
* Returns a random SVM address.
|
|
339
356
|
*/
|
|
@@ -358,8 +375,82 @@ export const createDefaultTransaction = async (rpcClient: SVMProvider, signer: T
|
|
|
358
375
|
);
|
|
359
376
|
};
|
|
360
377
|
|
|
378
|
+
/**
|
|
379
|
+
* Simulates a transaction and decodes the result using a parser function.
|
|
380
|
+
* @param solanaClient - The Solana client.
|
|
381
|
+
* @param ix - The instruction to simulate.
|
|
382
|
+
* @param signer - The signer of the transaction.
|
|
383
|
+
* @param parser - The parser function to decode the result.
|
|
384
|
+
* @returns The decoded result.
|
|
385
|
+
*/
|
|
386
|
+
export const simulateAndDecode = async <P extends (buf: Buffer) => unknown>(
|
|
387
|
+
solanaClient: SVMProvider,
|
|
388
|
+
ix: IInstruction,
|
|
389
|
+
signer: KeyPairSigner,
|
|
390
|
+
parser: P
|
|
391
|
+
): Promise<ReturnType<P>> => {
|
|
392
|
+
const simulationTx = appendTransactionMessageInstruction(ix, await createDefaultTransaction(solanaClient, signer));
|
|
393
|
+
|
|
394
|
+
const simulationResult = await solanaClient
|
|
395
|
+
.simulateTransaction(getBase64EncodedWireTransaction(await signTransactionMessageWithSigners(simulationTx)), {
|
|
396
|
+
encoding: "base64",
|
|
397
|
+
})
|
|
398
|
+
.send();
|
|
399
|
+
|
|
400
|
+
if (!simulationResult.value.returnData?.data[0]) {
|
|
401
|
+
throw new Error("No return data");
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
return parser(Buffer.from(simulationResult.value.returnData.data[0], "base64")) as ReturnType<P>;
|
|
405
|
+
};
|
|
406
|
+
|
|
407
|
+
/**
|
|
408
|
+
* Returns the PDA for the CCTP nonce.
|
|
409
|
+
* @param solanaClient The Solana client.
|
|
410
|
+
* @param signer The signer of the transaction.
|
|
411
|
+
* @param nonce The nonce to get the PDA for.
|
|
412
|
+
* @param sourceDomain The source domain.
|
|
413
|
+
* @returns The PDA for the CCTP nonce.
|
|
414
|
+
*/
|
|
415
|
+
export const getCCTPNoncePda = async (
|
|
416
|
+
solanaClient: SVMProvider,
|
|
417
|
+
signer: KeyPairSigner,
|
|
418
|
+
nonce: number,
|
|
419
|
+
sourceDomain: number
|
|
420
|
+
) => {
|
|
421
|
+
const [messageTransmitterPda] = await getProgramDerivedAddress({
|
|
422
|
+
programAddress: MessageTransmitterClient.MESSAGE_TRANSMITTER_PROGRAM_ADDRESS,
|
|
423
|
+
seeds: ["message_transmitter"],
|
|
424
|
+
});
|
|
425
|
+
const getNonceIx = await MessageTransmitterClient.getGetNoncePdaInstruction({
|
|
426
|
+
messageTransmitter: messageTransmitterPda,
|
|
427
|
+
nonce,
|
|
428
|
+
sourceDomain: sourceDomain,
|
|
429
|
+
});
|
|
430
|
+
|
|
431
|
+
const parserFunction = (buf: Buffer): Address => {
|
|
432
|
+
if (buf.length === 32) {
|
|
433
|
+
return address(bs58.encode(buf));
|
|
434
|
+
}
|
|
435
|
+
throw new Error("Invalid buffer");
|
|
436
|
+
};
|
|
437
|
+
|
|
438
|
+
return await simulateAndDecode(solanaClient, getNonceIx, signer, parserFunction);
|
|
439
|
+
};
|
|
440
|
+
|
|
441
|
+
/**
|
|
442
|
+
* Checks if a CCTP message is a deposit for burn event.
|
|
443
|
+
* @param event The CCTP message event.
|
|
444
|
+
* @returns True if the message is a deposit for burn event, false otherwise.
|
|
445
|
+
*/
|
|
446
|
+
export function isDepositForBurnEvent(event: AttestedCCTPMessage): boolean {
|
|
447
|
+
return event.type === "transfer";
|
|
448
|
+
}
|
|
449
|
+
|
|
361
450
|
/**
|
|
362
451
|
* Convert a bigint (0 ≤ n < 2^256) to a 32-byte Uint8Array (big-endian).
|
|
452
|
+
* @param n The bigint to convert.
|
|
453
|
+
* @returns The 32-byte Uint8Array.
|
|
363
454
|
*/
|
|
364
455
|
export function bigintToU8a32(n: bigint): Uint8Array {
|
|
365
456
|
if (n < BigInt(0) || n > ethers.constants.MaxUint256.toBigInt()) {
|
|
@@ -25,7 +25,8 @@ export async function getWidestPossibleExpectedBlockRange(
|
|
|
25
25
|
endBlockBuffers: number[],
|
|
26
26
|
clients: Clients,
|
|
27
27
|
latestMainnetBlock: number,
|
|
28
|
-
enabledChains: number[]
|
|
28
|
+
enabledChains: number[],
|
|
29
|
+
optimistic: boolean = false
|
|
29
30
|
): Promise<number[][]> {
|
|
30
31
|
// We impose a buffer on the head of the chain to increase the probability that the received blocks are final.
|
|
31
32
|
// Reducing the latest block that we query also gives partially filled deposits slightly more buffer for relayers
|
|
@@ -85,7 +86,9 @@ export async function getWidestPossibleExpectedBlockRange(
|
|
|
85
86
|
// Chain has advanced far enough including the buffer, return range from previous proposed end block + 1 to
|
|
86
87
|
// latest block for chain minus buffer.
|
|
87
88
|
return [
|
|
88
|
-
|
|
89
|
+
optimistic
|
|
90
|
+
? clients.hubPoolClient.getOptimisticBundleStartBlockNumber(chainIds, latestMainnetBlock, chainId)
|
|
91
|
+
: clients.hubPoolClient.getNextBundleStartBlockNumber(chainIds, latestMainnetBlock, chainId),
|
|
89
92
|
latestPossibleBundleEndBlockNumbers[index],
|
|
90
93
|
];
|
|
91
94
|
});
|
|
@@ -789,6 +789,29 @@ export class HubPoolClient extends BaseAbstractClient {
|
|
|
789
789
|
return endBlock > 0 ? endBlock + 1 : 0;
|
|
790
790
|
}
|
|
791
791
|
|
|
792
|
+
// @dev Returns the start block of the next bundle assuming that if there is a currently outstanding root bundle proposal, it will pass liveness.
|
|
793
|
+
getOptimisticBundleStartBlockNumber(chainIdList: number[], latestMainnetBlock: number, chainId: number): number {
|
|
794
|
+
// If there is no pending root bundle, then return block ranges based on the latest fully executed root bundle.
|
|
795
|
+
if (!this.hasPendingProposal()) {
|
|
796
|
+
return this.getNextBundleStartBlockNumber(chainIdList, latestMainnetBlock, chainId);
|
|
797
|
+
}
|
|
798
|
+
// We cannot normally index `this.proposedRootBundles` since a bundle there may have been previously disputed, so only index `this.proposedRootBundles`
|
|
799
|
+
// if we have a pending proposal, since this must mean that the pending root bundle is the most recent proposed root bundle.
|
|
800
|
+
const latestProposedBundle = this.proposedRootBundles[this.proposedRootBundles.length - 1];
|
|
801
|
+
|
|
802
|
+
// If there is no previous root bundle, then return 0.
|
|
803
|
+
if (!isDefined(latestProposedBundle)) {
|
|
804
|
+
return 0;
|
|
805
|
+
}
|
|
806
|
+
|
|
807
|
+
// Otherwise, get the bundle end block for the optimistic bundle.
|
|
808
|
+
const optimisticEndBlock = this.getBundleEndBlockForChain(latestProposedBundle, chainId, chainIdList);
|
|
809
|
+
|
|
810
|
+
// As above, this assumes that chain ID's are only added to the chain ID list over time, and that chains are never
|
|
811
|
+
// deleted.
|
|
812
|
+
return optimisticEndBlock > 0 ? optimisticEndBlock + 1 : 0;
|
|
813
|
+
}
|
|
814
|
+
|
|
792
815
|
getLatestExecutedRootBundleContainingL1Token(
|
|
793
816
|
block: number,
|
|
794
817
|
chain: number,
|