@@ -201,165 +201,45 @@ class TestBiz {
201201 }
202202
203203 @Test
204- fun testBizEncodeRegisterSessionCall () {
205- val publicKey = PublicKey (
206- " 0x041c05286fe694493eae33312f2d2e0d0abeda8db76238b7a204be1fb87f54ce4228fef61ef4ac300f631657635c28e59bfb2fe71bce1634c81c65642042f6dc4d" .toHexByteArray(),
207- PublicKeyType .NIST256P1EXTENDED
208- )
209- val validUntil = " 0x15181" // 86_401
210- val encoded = WCBiz .encodeRegisterSessionCall(publicKey, Numeric .hexStringToByteArray(validUntil))
211- assertEquals(
212- " 0x826491fb000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000151810000000000000000000000000000000000000000000000000000000000000041041c05286fe694493eae33312f2d2e0d0abeda8db76238b7a204be1fb87f54ce4228fef61ef4ac300f631657635c28e59bfb2fe71bce1634c81c65642042f6dc4d00000000000000000000000000000000000000000000000000000000000000" ,
213- Numeric .toHexString(encoded)
214- )
215- }
216-
217- @Test
218- fun testBizEncodeRemoveSessionCall () {
219- val publicKey = PublicKey (
220- " 0x041c05286fe694493eae33312f2d2e0d0abeda8db76238b7a204be1fb87f54ce4228fef61ef4ac300f631657635c28e59bfb2fe71bce1634c81c65642042f6dc4d" .toHexByteArray(),
221- PublicKeyType .NIST256P1EXTENDED
222- )
223- val encoded = WCBiz .encodeRemoveSessionCall(publicKey)
224- assertEquals(
225- " 0xe1c06abd00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000041041c05286fe694493eae33312f2d2e0d0abeda8db76238b7a204be1fb87f54ce4228fef61ef4ac300f631657635c28e59bfb2fe71bce1634c81c65642042f6dc4d00000000000000000000000000000000000000000000000000000000000000" ,
226- Numeric .toHexString(encoded)
227- )
228- }
229-
230- @Test
231- fun testBizEncodePasskeyNonce () {
232- val nonce = " 0x7b" // 123
233- val passkeyNonce = WCBiz .encodePasskeySessionNonce(Numeric .hexStringToByteArray(nonce))
234- assertEquals(
235- " 0x00000000000000000000000000000000050041d6a66939a8000000000000007b" ,
236- Numeric .toHexString(passkeyNonce)
237- )
238- }
239-
240- @Test
241- fun testSignUserOperationV7WithPasskeySession () {
242- val chainIdByteArray = " 0x7A69" .toHexByteArray() // 31337
243- val wallet = " 0x336Cd992a83242D91f556C1F7e855AcA366193e0"
244- val bizPasskeySessionAccount = " 0xa0Cb889707d426A7A386870A03bc70d1b0697598"
245- val bizPasskeySessionCodeName = " PasskeySession"
246- val codeVersion = " v1.0.0"
247- // keccak("PasskeySession(bytes32 userOpHash)")
248- val typeHash = " 0x3463fe66e4d03af5b942aebde2b191eff52d291c0a2c8cc302d786854f34bfc9"
249- // keccak("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract,bytes32 salt)")
250- val domainSeparatorHash = " 0xd87cd6ef79d4e2b95e15ce8abf732db51ec771f1ca2edccf22a46c729ac56472"
251-
252- // Step 1. Generate UserOperation and its hash.
253-
254- val batch = Ethereum .Transaction .SCWalletBatch .newBuilder()
255- .setWalletType(Ethereum .SCWalletType .Biz4337 )
256- .addCalls(Ethereum .Transaction .SCWalletBatch .BatchedCall .newBuilder().apply {
257- address = " 0x0000000000000000000000000000000000000001"
258- amount = ByteString .copyFrom(" 0xde0b6b3a7640000" .toHexByteArray())
259- payload = ByteString .EMPTY
260- })
261- .addCalls(Ethereum .Transaction .SCWalletBatch .BatchedCall .newBuilder().apply {
262- address = " 0x0000000000000000000000000000000000000002"
263- amount = ByteString .copyFrom(" 0xde0b6b3a7640000" .toHexByteArray())
264- payload = ByteString .EMPTY
265- })
266- .addCalls(Ethereum .Transaction .SCWalletBatch .BatchedCall .newBuilder().apply {
267- address = " 0x0000000000000000000000000000000000000003"
268- amount = ByteString .copyFrom(" 0xde0b6b3a7640000" .toHexByteArray())
269- payload = ByteString .EMPTY
270- })
271- .build()
272-
273- val actualNonce = Numeric .hexStringToByteArray(" 0x01" )
274- val passkeyNonce = WCBiz .encodePasskeySessionNonce(actualNonce)
275-
276- val userOpV07 = Ethereum .UserOperationV0_7 .newBuilder().apply {
277- entryPoint = " 0x0000000071727De22E5E9d8BAf0edAc6f37da032"
278- sender = wallet
279- preVerificationGas = ByteString .copyFrom(" 0x186a0" .toHexByteArray()) // 100000
280- verificationGasLimit = ByteString .copyFrom(" 0x186a0" .toHexByteArray()) // 100000
281- paymaster = " 0xf62849f9a0b5bf2913b396098f7c7019b51a820a"
282- paymasterVerificationGasLimit = ByteString .copyFrom(" 0x1869F" .toHexByteArray()) // 99999
283- paymasterPostOpGasLimit = ByteString .copyFrom(" 0x15B38" .toHexByteArray()) // 88888
284- paymasterData = ByteString .copyFrom(
285- " 0x00000000000b0000000000002e234dae75c793f67a35089c9d99245e1c58470b00000000000000000000000000000000000000000000000000000000000186a0072f35038bcacc31bcdeda87c1d9857703a26fb70a053f6e87da5a4e7a1e1f3c4b09fbe2dbff98e7a87ebb45a635234f4b79eff3225d07560039c7764291c97e1b"
286- .toHexByteArray()
287- )
288- }.build()
289-
290- // Create signing input
291- val signingInput = Ethereum .SigningInput .newBuilder().apply {
292- // Dummy private key.
293- privateKey = ByteString .copyFrom(" 0x03d99692017473e2d631945a812607b23269d85721e0f370b8d3e7d29a874fd2" .toHexByteArray())
294- chainId = ByteString .copyFrom(chainIdByteArray) // 31337
295- nonce = ByteString .copyFrom(passkeyNonce)
296- txMode = Ethereum .TransactionMode .UserOp
297- gasLimit = ByteString .copyFrom(" 0x186a0" .toHexByteArray()) // 100000
298- maxFeePerGas = ByteString .copyFrom(" 0x186A0" .toHexByteArray()) // 100000
299- maxInclusionFeePerGas = ByteString .copyFrom(" 0x186A0" .toHexByteArray()) // 100000
300- toAddress = " 0x61061fCAE11fD5461535e134EfF67A98CFFF44E9"
204+ fun testBizSignExecuteWithSignatureCall () {
205+ // Create ERC20 approve function call
206+ val approveFunc = EthereumAbiFunction (" approve" )
207+ approveFunc.addParamAddress(" 0xBC472b43BC237f733c78a581078F58A6a89c46Ec" .toHexByteArray(), false )
208+ approveFunc.addParamUInt256(" 0x03e8" .toHexByteArray(), false ) // 1000
209+ val approvePayload = EthereumAbi .encode(approveFunc)
210+
211+ // Build the ExecuteWithSignatureInput protobuf
212+ val input = wallet.core.jni.proto.Biz .ExecuteWithSignatureInput .newBuilder()
213+ input.apply {
214+ // Add execution
215+ addExecutions(wallet.core.jni.proto.Biz .Execution .newBuilder().apply {
216+ address = " 0x4B0F1812e5Df2A09796481Ff14017e6005508003" // TWT token
217+ amount = ByteString .copyFrom(" 0x00" .toHexByteArray())
218+ payload = ByteString .copyFrom(approvePayload)
219+ }.build())
220+
221+ // Private key
222+ privateKey = ByteString .copyFrom(" 0xefec50f00ef0c09d967f3e363ee96502ce18a1881f6ac22321aa58071d43c66f" .toHexByteArray())
223+
224+ // Nonce
225+ nonce = ByteString .copyFrom(" 0x00" .toHexByteArray())
301226
302- transaction = Ethereum .Transaction .newBuilder().apply {
303- scwBatch = batch
227+ // Encoding hash params
228+ encodingHashParams = wallet.core.jni.proto.Biz .EncodingHashParams .newBuilder().apply {
229+ chainId = ByteString .copyFrom(" 0x38" .toHexByteArray()) // 56 (BSC)
230+ codeAddress = " 0xba083F0EeAF806603d31582D4e7667fB5A4A1B30"
231+ codeName = " Biz"
232+ codeVersion = " v1.0.0"
233+ typeHash = " 0xec429430bbd6d0e373848272230d6fe2bac6319d903762e089c5cae97af53df0"
234+ domainSeparatorHash = " 0xd87cd6ef79d4e2b95e15ce8abf732db51ec771f1ca2edccf22a46c729ac56472"
304235 }.build()
236+ }
305237
306- userOperationV07 = userOpV07
307- }.build()
308-
309- val output = AnySigner .sign(signingInput, CoinType .ETHEREUM , Ethereum .SigningOutput .parser())
310-
311- val userOpHash = output.preHash.toByteArray()
312- assertEquals(
313- " 0x7762e85586107f2bca787a9163b71f0584eabd84258a93cca0e896589a193571" ,
314- Numeric .toHexString(userOpHash)
315- )
316-
317- // Step 2. Encode UserOperation hash in Biz format.
318-
319- val encodedUserOpHash = WCBiz .getEncodedHash(
320- chainIdByteArray,
321- bizPasskeySessionAccount,
322- bizPasskeySessionCodeName,
323- codeVersion,
324- typeHash,
325- domainSeparatorHash,
326- wallet,
327- Numeric .toHexString(userOpHash)
328- )
329- assertEquals(
330- " 0x7d130331f16bb3d2bc3d72db02879d0745d4452592e56723de8b27cf1ee006c7" ,
331- Numeric .toHexString(encodedUserOpHash)
332- )
333-
334- // Step 3. Generate a WebAuthn with the given challenge (encodedUserOpHash) and passkey signature.
335-
336- val clientJsonFirstPart = " {\" type\" :\" webauthn.get\" ,\" challenge\" :\" "
337- val challengeBase64 = Base64 .encodeUrl(encodedUserOpHash)
338- val challengeBase64NoPad = challengeBase64.trimEnd(' =' )
339- val clientJsonLastPart = " \" ,\" origin\" :\" https://sign.coinbase.com\" ,\" crossOrigin\" :false}"
340- val clientJson = clientJsonFirstPart + challengeBase64NoPad + clientJsonLastPart
341- assertEquals(clientJson, " {\" type\" :\" webauthn.get\" ,\" challenge\" :\" fRMDMfFrs9K8PXLbAoedB0XURSWS5Wcj3osnzx7gBsc\" ,\" origin\" :\" https://sign.coinbase.com\" ,\" crossOrigin\" :false}" )
342-
343- // Authenticator data for Chrome Profile touchID signature
344- val authenticatorData = " 0x49960de5880e8c687434170f6476605b8fe4aeb9a28632c7995cf3ba831d97630500000000"
345- // Signature computed on a device using passkey.
346- val derSignature = " 0x3045022073f8762dd6fb0eb39aea829525658fca612d1c433db6381c9d63a52ee15a26be022100e091f452b74519a2894a96d142bdd1888ac6513f5dff76e48c0312144ef9b382" .toHexByteArray()
347-
348- val passkeySignature = WebAuthnSolidity .getFormattedSignature(authenticatorData, clientJson, derSignature)
349- assertEquals(
350- "0x000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000017000000000000000000000000000000000000000000000000000000000000000173f8762dd6fb0eb39aea829525658fca612d1c433db6381c9d63a52ee15a26be1f6e0bac48bae65e76b5692ebd422e773220a96e491827a067b6b8aead6971cf000000000000000000000000000000000000000000000000000000000000002549960de5880e8c687434170f6476605b8fe4aeb9a28632c7995cf3ba831d97630500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008a7b2274797065223a22776562617574686e2e676574222c226368616c6c656e6765223a2266524d444d66467273394b3850584c62416f656442305855525357533557636a336f736e7a783767427363222c226f726967696e223a2268747470733a2f2f7369676e2e636f696e626173652e636f6d222c2263726f73734f726967696e223a66616c73657d00000000000000000000000000000000000000000000",
351- Numeric .toHexString(passkeySignature)
352- )
353-
354- // Step 4. Final step. Biz-specific adjustments.
238+ // Call the native function
239+ val encoded = WCBiz .signExecuteWithSignatureCall(input.build().toByteArray())
355240
356- // `passkeyIndex` can be gotten by using `Biz.getPasskeySessionIndexForValidation()` view function.
357- // https://github.com/trustwallet/7702-passkey-session/blob/b5c85a5c9a72c19195d1d60a1c90b3908a6a0371/src/BizPasskeySession.sol#L412
358- val passkeyIndex: Byte = 0x00
359- val passkeyIndexAttachedSignature = byteArrayOf(passkeyIndex) + passkeySignature
360- assertEquals(
361- "0x00000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000017000000000000000000000000000000000000000000000000000000000000000173f8762dd6fb0eb39aea829525658fca612d1c433db6381c9d63a52ee15a26be1f6e0bac48bae65e76b5692ebd422e773220a96e491827a067b6b8aead6971cf000000000000000000000000000000000000000000000000000000000000002549960de5880e8c687434170f6476605b8fe4aeb9a28632c7995cf3ba831d97630500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008a7b2274797065223a22776562617574686e2e676574222c226368616c6c656e6765223a2266524d444d66467273394b3850584c62416f656442305855525357533557636a336f736e7a783767427363222c226f726967696e223a2268747470733a2f2f7369676e2e636f696e626173652e636f6d222c2263726f73734f726967696e223a66616c73657d00000000000000000000000000000000000000000000",
362- Numeric .toHexString(passkeyIndexAttachedSignature)
363- )
241+ // Verify the result
242+ val expected = " 0x1d92e4b600000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000160000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000004b0f1812e5df2a09796481ff14017e6005508003000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044095ea7b3000000000000000000000000bc472b43bc237f733c78a581078f58a6a89c46ec00000000000000000000000000000000000000000000000000000000000003e8000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041db18e3a0150ddef964810e480b25592942a22d0b583f7d5cbb33ef6fb4baa66e753af78e967ee374070e16cf963a6cd7a3adb713e50d553aefbc361c48366a101b00000000000000000000000000000000000000000000000000000000000000"
243+ assertEquals(expected, Numeric .toHexString(encoded))
364244 }
365245}
0 commit comments