|
1 | 1 | import { BN } from "@coral-xyz/anchor";
|
2 | 2 | import { ethers } from "ethers";
|
3 | 3 | import { RelayerRefundLeaf, RelayerRefundLeafSolana, SlowFillLeaf } from "../types/svm";
|
| 4 | +import { serialize } from "borsh"; |
4 | 5 |
|
5 | 6 | /**
|
6 | 7 | * Calculates the relay hash from relay data and chain ID.
|
@@ -75,36 +76,56 @@ export function hashNonEmptyMessage(message: Buffer) {
|
75 | 76 | return new Uint8Array(32);
|
76 | 77 | }
|
77 | 78 |
|
| 79 | +/** |
| 80 | + * Class for relay data. |
| 81 | + */ |
| 82 | +class RelayData { |
| 83 | + constructor(properties: any) { |
| 84 | + Object.assign(this, properties); |
| 85 | + } |
| 86 | +} |
| 87 | + |
| 88 | +/** |
| 89 | + * Schema for relay data. |
| 90 | + */ |
| 91 | +const relayDataSchema = new Map([ |
| 92 | + [ |
| 93 | + RelayData, |
| 94 | + { |
| 95 | + kind: "struct", |
| 96 | + fields: [ |
| 97 | + ["amountToReturn", "u64"], |
| 98 | + ["chainId", "u64"], |
| 99 | + ["refundAmounts", ["u64"]], |
| 100 | + ["leafId", "u32"], |
| 101 | + ["mintPublicKey", [32]], |
| 102 | + ["refundAddresses", [[32]]], |
| 103 | + ], |
| 104 | + }, |
| 105 | + ], |
| 106 | +]); |
| 107 | + |
78 | 108 | /**
|
79 | 109 | * Calculates the relayer refund leaf hash for Solana.
|
80 | 110 | */
|
81 | 111 | export function calculateRelayerRefundLeafHashUint8Array(relayData: RelayerRefundLeafSolana): string {
|
82 |
| - const refundAmountsBuffer = Buffer.concat( |
83 |
| - relayData.refundAmounts.map((amount) => { |
84 |
| - const buf = Buffer.alloc(8); |
85 |
| - amount.toArrayLike(Buffer, "le", 8).copy(buf); |
86 |
| - return buf; |
87 |
| - }) |
88 |
| - ); |
| 112 | + const refundAddresses = relayData.refundAddresses.map((address) => address.toBuffer()); |
89 | 113 |
|
90 |
| - const refundAddressesBuffer = Buffer.concat(relayData.refundAddresses.map((address) => address.toBuffer())); |
| 114 | + const data = new RelayData({ |
| 115 | + amountToReturn: relayData.amountToReturn, |
| 116 | + chainId: relayData.chainId, |
| 117 | + refundAmounts: relayData.refundAmounts, |
| 118 | + leafId: relayData.leafId, |
| 119 | + mintPublicKey: relayData.mintPublicKey.toBuffer(), |
| 120 | + refundAddresses: refundAddresses, |
| 121 | + }); |
91 | 122 |
|
92 |
| - // TODO: We better consider reusing Borch serializer in production. |
93 |
| - const contentToHash = Buffer.concat([ |
94 |
| - // SVM leaves require the first 64 bytes to be 0 to ensure EVM leaves can never be played on SVM and vice versa. |
95 |
| - Buffer.alloc(64, 0), |
96 |
| - relayData.amountToReturn.toArrayLike(Buffer, "le", 8), |
97 |
| - relayData.chainId.toArrayLike(Buffer, "le", 8), |
98 |
| - new BN(relayData.refundAmounts.length).toArrayLike(Buffer, "le", 4), |
99 |
| - refundAmountsBuffer, |
100 |
| - relayData.leafId.toArrayLike(Buffer, "le", 4), |
101 |
| - relayData.mintPublicKey.toBuffer(), |
102 |
| - new BN(relayData.refundAddresses.length).toArrayLike(Buffer, "le", 4), |
103 |
| - refundAddressesBuffer, |
104 |
| - ]); |
| 123 | + const serializedData = serialize(relayDataSchema, data); |
105 | 124 |
|
106 |
| - const relayHash = ethers.utils.keccak256(contentToHash); |
107 |
| - return relayHash; |
| 125 | + // SVM leaves require the first 64 bytes to be 0 to ensure EVM leaves can never be played on SVM and vice versa. |
| 126 | + const contentToHash = Buffer.concat([Buffer.alloc(64, 0), serializedData]); |
| 127 | + |
| 128 | + return ethers.utils.keccak256(contentToHash); |
108 | 129 | }
|
109 | 130 |
|
110 | 131 | /**
|
@@ -134,31 +155,68 @@ export const relayerRefundHashFn = (input: RelayerRefundLeaf | RelayerRefundLeaf
|
134 | 155 | }
|
135 | 156 | };
|
136 | 157 |
|
| 158 | +/** |
| 159 | + * Class for slow fill data. |
| 160 | + */ |
| 161 | +class SlowFillData { |
| 162 | + constructor(properties: any) { |
| 163 | + Object.assign(this, properties); |
| 164 | + } |
| 165 | +} |
| 166 | + |
| 167 | +/** |
| 168 | + * Schema for slow fill data. |
| 169 | + */ |
| 170 | +const slowFillDataSchema = new Map([ |
| 171 | + [ |
| 172 | + SlowFillData, |
| 173 | + { |
| 174 | + kind: "struct", |
| 175 | + fields: [ |
| 176 | + ["depositor", [32]], |
| 177 | + ["recipient", [32]], |
| 178 | + ["exclusiveRelayer", [32]], |
| 179 | + ["inputToken", [32]], |
| 180 | + ["outputToken", [32]], |
| 181 | + ["inputAmount", "u64"], |
| 182 | + ["outputAmount", "u64"], |
| 183 | + ["originChainId", "u64"], |
| 184 | + ["depositId", [32]], |
| 185 | + ["fillDeadline", "u32"], |
| 186 | + ["exclusivityDeadline", "u32"], |
| 187 | + ["message", ["u8"]], |
| 188 | + ["chainId", "u64"], |
| 189 | + ["updatedOutputAmount", "u64"], |
| 190 | + ], |
| 191 | + }, |
| 192 | + ], |
| 193 | +]); |
| 194 | + |
137 | 195 | /**
|
138 | 196 | * Hash function for slow fill leaves.
|
139 | 197 | */
|
140 |
| -// TODO: We better consider reusing Borch serializer in production. |
141 | 198 | export function slowFillHashFn(slowFillLeaf: SlowFillLeaf): string {
|
142 |
| - const contentToHash = Buffer.concat([ |
143 |
| - // SVM leaves require the first 64 bytes to be 0 to ensure EVM leaves can never be played on SVM and vice versa. |
144 |
| - Buffer.alloc(64, 0), |
145 |
| - slowFillLeaf.relayData.depositor.toBuffer(), |
146 |
| - slowFillLeaf.relayData.recipient.toBuffer(), |
147 |
| - slowFillLeaf.relayData.exclusiveRelayer.toBuffer(), |
148 |
| - slowFillLeaf.relayData.inputToken.toBuffer(), |
149 |
| - slowFillLeaf.relayData.outputToken.toBuffer(), |
150 |
| - slowFillLeaf.relayData.inputAmount.toArrayLike(Buffer, "le", 8), |
151 |
| - slowFillLeaf.relayData.outputAmount.toArrayLike(Buffer, "le", 8), |
152 |
| - slowFillLeaf.relayData.originChainId.toArrayLike(Buffer, "le", 8), |
153 |
| - Buffer.from(slowFillLeaf.relayData.depositId), |
154 |
| - new BN(slowFillLeaf.relayData.fillDeadline).toArrayLike(Buffer, "le", 4), |
155 |
| - new BN(slowFillLeaf.relayData.exclusivityDeadline).toArrayLike(Buffer, "le", 4), |
156 |
| - new BN(slowFillLeaf.relayData.message.length).toArrayLike(Buffer, "le", 4), |
157 |
| - slowFillLeaf.relayData.message, |
158 |
| - slowFillLeaf.chainId.toArrayLike(Buffer, "le", 8), |
159 |
| - slowFillLeaf.updatedOutputAmount.toArrayLike(Buffer, "le", 8), |
160 |
| - ]); |
| 199 | + const data = new SlowFillData({ |
| 200 | + depositor: Uint8Array.from(slowFillLeaf.relayData.depositor.toBuffer()), |
| 201 | + recipient: Uint8Array.from(slowFillLeaf.relayData.recipient.toBuffer()), |
| 202 | + exclusiveRelayer: Uint8Array.from(slowFillLeaf.relayData.exclusiveRelayer.toBuffer()), |
| 203 | + inputToken: Uint8Array.from(slowFillLeaf.relayData.inputToken.toBuffer()), |
| 204 | + outputToken: Uint8Array.from(slowFillLeaf.relayData.outputToken.toBuffer()), |
| 205 | + inputAmount: slowFillLeaf.relayData.inputAmount, |
| 206 | + outputAmount: slowFillLeaf.relayData.outputAmount, |
| 207 | + originChainId: slowFillLeaf.relayData.originChainId, |
| 208 | + depositId: Uint8Array.from(Buffer.from(slowFillLeaf.relayData.depositId)), |
| 209 | + fillDeadline: slowFillLeaf.relayData.fillDeadline, |
| 210 | + exclusivityDeadline: slowFillLeaf.relayData.exclusivityDeadline, |
| 211 | + message: Uint8Array.from(slowFillLeaf.relayData.message), |
| 212 | + chainId: slowFillLeaf.chainId, |
| 213 | + updatedOutputAmount: slowFillLeaf.updatedOutputAmount, |
| 214 | + }); |
| 215 | + |
| 216 | + const serializedData = serialize(slowFillDataSchema, data); |
| 217 | + |
| 218 | + // SVM leaves require the first 64 bytes to be 0 to ensure EVM leaves cannot be played on SVM and vice versa |
| 219 | + const contentToHash = Buffer.concat([Buffer.alloc(64, 0), serializedData]); |
161 | 220 |
|
162 |
| - const slowFillHash = ethers.utils.keccak256(contentToHash); |
163 |
| - return slowFillHash; |
| 221 | + return ethers.utils.keccak256(contentToHash); |
164 | 222 | }
|
0 commit comments