Skip to content

Commit a76acae

Browse files
committed
Decorate wallet transactions for subsigners
1 parent 7fa0984 commit a76acae

File tree

10 files changed

+182
-56
lines changed

10 files changed

+182
-56
lines changed

packages/guard/src/signer.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,12 @@ export class GuardSigner implements signers.SapientSigner {
2525
return this.address
2626
}
2727

28+
async decorateTransactions(
29+
bundle: commons.transaction.IntendedTransactionBundle,
30+
): Promise<commons.transaction.IntendedTransactionBundle> {
31+
return Promise.resolve(bundle)
32+
}
33+
2834
async requestSignature(
2935
id: string,
3036
_message: BytesLike,

packages/signhub/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
"test:coverage": "nyc yarn test"
1515
},
1616
"dependencies": {
17+
"@0xsequence/core": "workspace:*",
1718
"ethers": "^5.5.2"
1819
},
1920
"peerDependencies": {},

packages/signhub/src/orchestrator.ts

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { ethers } from "ethers"
2+
import { commons } from "@0xsequence/core"
23
import { isSapientSigner, SapientSigner } from "./signers/signer"
34
import { SignerWrapper } from "./signers/wrapper"
45

@@ -39,10 +40,11 @@ export function isSignerStatusPending(status: SignerStatus): status is SignerSta
3940
export const InitialSituation = "Initial"
4041

4142
/**
42-
* It orchestrates the signing of a single digest by multiple signers.
43+
* Orchestrates the signing of a single digests and transactions by multiple signers.
4344
* It can provide internal visibility of the signing process, and it also
4445
* provides the internal signers with additional information about the
45-
* message being signed.
46+
* message being signed. Transaction decoration can be used to ensure on-chain state
47+
* is correctly managed during the signing process.
4648
*/
4749
export class Orchestrator {
4850
private observers: ((status: Status, metadata: Object) => void)[] = []
@@ -82,6 +84,15 @@ export class Orchestrator {
8284
])
8385
}
8486

87+
async decorateTransactions(
88+
bundle: commons.transaction.IntendedTransactionBundle,
89+
): Promise<commons.transaction.IntendedTransactionBundle> {
90+
for (const signer of this.signers) {
91+
bundle = await signer.decorateTransactions(bundle)
92+
}
93+
return bundle
94+
}
95+
8596
signMessage(args: {
8697
candidates?: string[],
8798
message: ethers.BytesLike,

packages/signhub/src/signers/signer.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,20 @@
11
import { ethers } from "ethers"
2+
import { commons } from "@0xsequence/core"
23
import { Status } from "../orchestrator"
34

45
export interface SapientSigner {
56
getAddress(): Promise<string>
67

8+
/**
9+
* Modify the transaction bundle before it is sent.
10+
*/
11+
decorateTransactions(
12+
bundle: commons.transaction.IntendedTransactionBundle,
13+
): Promise<commons.transaction.IntendedTransactionBundle>
14+
15+
/**
16+
* Request a signature from the signer.
17+
*/
718
requestSignature(
819
id: string,
920
message: ethers.BytesLike,
@@ -15,6 +26,9 @@ export interface SapientSigner {
1526
}
1627
): Promise<boolean>
1728

29+
/**
30+
* Notify the signer of a status change.
31+
*/
1832
notifyStatusChange(
1933
id: string,
2034
status: Status,

packages/signhub/src/signers/wrapper.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11

22
import { ethers } from 'ethers'
3+
import { commons } from "@0xsequence/core"
34
import { Status } from '../orchestrator'
45
import { SapientSigner } from './signer'
56

@@ -10,6 +11,12 @@ export class SignerWrapper implements SapientSigner {
1011
return this.signer.getAddress()
1112
}
1213

14+
async decorateTransactions(
15+
bundle: commons.transaction.IntendedTransactionBundle
16+
): Promise<commons.transaction.IntendedTransactionBundle> {
17+
return bundle
18+
}
19+
1320
async requestSignature(
1421
_id: string,
1522
message: ethers.BytesLike,

packages/signhub/tests/orchestrator.spec.ts

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11

22
import * as chai from 'chai'
33
import { ethers } from 'ethers'
4+
import { commons } from "@0xsequence/core"
45
import { isSignerStatusPending, isSignerStatusRejected, isSignerStatusSigned, Orchestrator, Status } from '../src'
56
import { SapientSigner } from '../src/signers'
67

@@ -109,6 +110,11 @@ describe('Orchestrator', () => {
109110
getAddress: async function (): Promise<string> {
110111
return brokenSignerEOA.address
111112
},
113+
decorateTransactions(
114+
bundle: commons.transaction.IntendedTransactionBundle,
115+
): Promise<commons.transaction.IntendedTransactionBundle> {
116+
throw new Error('This is a broken signer.')
117+
},
112118
requestSignature: async function (
113119
id: string,
114120
message: ethers.utils.BytesLike,
@@ -189,6 +195,11 @@ describe('Orchestrator', () => {
189195
getAddress: async function (): Promise<string> {
190196
return rejectSignerEOA.address
191197
},
198+
decorateTransactions(
199+
bundle: commons.transaction.IntendedTransactionBundle,
200+
): Promise<commons.transaction.IntendedTransactionBundle> {
201+
throw new Error('This is a rejected signer.')
202+
},
192203
requestSignature: async function (
193204
id: string,
194205
message: ethers.utils.BytesLike,
@@ -241,6 +252,11 @@ describe('Orchestrator', () => {
241252
getAddress: async function (): Promise<string> {
242253
return '0x1234'
243254
},
255+
decorateTransactions(
256+
bundle: commons.transaction.IntendedTransactionBundle,
257+
): Promise<commons.transaction.IntendedTransactionBundle> {
258+
return Promise.resolve(bundle)
259+
},
244260
requestSignature: async function (
245261
id: string,
246262
message: ethers.utils.BytesLike,
@@ -269,6 +285,11 @@ describe('Orchestrator', () => {
269285
getAddress: async function (): Promise<string> {
270286
return '0x1234'
271287
},
288+
decorateTransactions(
289+
bundle: commons.transaction.IntendedTransactionBundle,
290+
): Promise<commons.transaction.IntendedTransactionBundle> {
291+
return Promise.resolve(bundle)
292+
},
272293
requestSignature: async function (
273294
id: string,
274295
message: ethers.utils.BytesLike,
@@ -301,6 +322,11 @@ describe('Orchestrator', () => {
301322
getAddress: async function (): Promise<string> {
302323
return '0x1234'
303324
},
325+
decorateTransactions(
326+
bundle: commons.transaction.IntendedTransactionBundle,
327+
): Promise<commons.transaction.IntendedTransactionBundle> {
328+
return Promise.resolve(bundle)
329+
},
304330
requestSignature: async function (
305331
id: string,
306332
message: ethers.utils.BytesLike,
@@ -331,6 +357,11 @@ describe('Orchestrator', () => {
331357
getAddress: async function (): Promise<string> {
332358
return '0x5678'
333359
},
360+
decorateTransactions(
361+
bundle: commons.transaction.IntendedTransactionBundle,
362+
): Promise<commons.transaction.IntendedTransactionBundle> {
363+
return Promise.resolve(bundle)
364+
},
334365
requestSignature: async function (
335366
id: string,
336367
message: ethers.utils.BytesLike,
@@ -383,6 +414,11 @@ describe('Orchestrator', () => {
383414
getAddress: async function (): Promise<string> {
384415
return '0x1234'
385416
},
417+
decorateTransactions(
418+
bundle: commons.transaction.IntendedTransactionBundle,
419+
): Promise<commons.transaction.IntendedTransactionBundle> {
420+
return Promise.resolve(bundle)
421+
},
386422
requestSignature: async function (
387423
id: string,
388424
message: ethers.utils.BytesLike,

packages/wallet/src/orchestrator/wrapper.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,12 @@ export class SequenceOrchestratorWrapper implements signers.SapientSigner {
1313
return this.wallet.address
1414
}
1515

16+
async decorateTransactions(
17+
bundle: commons.transaction.IntendedTransactionBundle
18+
): Promise<commons.transaction.IntendedTransactionBundle> {
19+
return this.wallet.decorateTransactions(bundle)
20+
}
21+
1622
async requestSignature(
1723
_id: string,
1824
message: ethers.utils.BytesLike,

packages/wallet/src/wallet.ts

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,12 @@ export class Wallet<
141141
async decorateTransactions(
142142
bundle: commons.transaction.IntendedTransactionBundle
143143
): Promise<commons.transaction.IntendedTransactionBundle> {
144-
if (await this.reader().isDeployed(this.address)) return bundle
144+
const decorated = await this.orchestrator.decorateTransactions(bundle)
145+
const unchanged = decorated === bundle
146+
147+
if (unchanged && await this.reader().isDeployed(this.address)) {
148+
return bundle
149+
}
145150

146151
const deployTx = this.buildDeployTransaction()
147152

@@ -151,12 +156,12 @@ export class Wallet<
151156
return {
152157
entrypoint: this.context.guestModule,
153158
chainId: this.chainId,
154-
intent: bundle.intent,
159+
intent: decorated.intent,
155160
transactions: [
156161
...deployTx.transactions,
157162
{
158-
to: bundle.entrypoint,
159-
data: commons.transaction.encodeBundleExecData(bundle),
163+
to: decorated.entrypoint,
164+
data: commons.transaction.encodeBundleExecData(decorated),
160165
gasLimit: 0,
161166
delegateCall: false,
162167
revertOnError: true,

0 commit comments

Comments
 (0)