Skip to content

Commit

Permalink
Merge pull request #175 from ar-io/PE-6432-signer-fixes
Browse files Browse the repository at this point in the history
feat(signing): add window arweave wallet to available signing options
  • Loading branch information
atticusofsparta authored Jul 29, 2024
2 parents 700c39b + 3b23f80 commit 8497a20
Show file tree
Hide file tree
Showing 12 changed files with 104 additions and 105 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ jobs:
cache: 'yarn'

- name: Install dependencies
run: yarn --immutable --immutable-cache
run: yarn --immutable --immutable-cache --ignore-engines

- run: yarn ${{ matrix.command }}

Expand All @@ -41,7 +41,7 @@ jobs:
cache: 'yarn'

- name: Install dependencies
run: yarn --immutable --immutable-cache
run: yarn --immutable --immutable-cache --ignore-engines

- name: Run tests
run: yarn test
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ jobs:
node-version-file: .nvmrc

- name: Install dependencies
run: yarn --immutable --immutable-cache
run: yarn --immutable --immutable-cache --ignore-engines

- name: Build
run: yarn build
Expand Down
16 changes: 11 additions & 5 deletions examples/esm/index.mjs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import {
ANT,
ArNSEventEmitter,
ArweaveSigner,
IO,
createAoSigner,
ioDevnetProcessId,
spawnANT,
} from '@ar.io/sdk';
Expand Down Expand Up @@ -66,10 +68,14 @@ import Arweave from 'arweave';
});

const jwk = await arweave.wallets.generate();

const processId = await spawnANT({
signer: new ArweaveSigner(jwk),
});
let processId;
try {
processId = await spawnANT({
signer: createAoSigner(new ArweaveSigner(jwk)),
});
} catch (error) {
console.log(error);
}

const ant = ANT.init({
processId,
Expand All @@ -92,7 +98,7 @@ import Arweave from 'arweave';

// fetching ants owned by a wallet using an event emitter
const address = 'ZjmB2vEUlHlJ7-rgJkYP09N5IzLPhJyStVrK5u9dDEo';
const processEmitter = new ArNSNameEmitter({ contract: arIO });
const processEmitter = new ArNSEventEmitter({ contract: arIO });
processEmitter.on('error', (e) => {
console.error(e);
});
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@
"@types/sinon": "^10.0.15",
"@typescript-eslint/eslint-plugin": "^5.62.0",
"@typescript-eslint/parser": "^6.4.0",
"arconnect": "^1.0.3",
"dotenv": "^16.4.5",
"dotenv-cli": "^7.4.2",
"esbuild": "^0.19.2",
Expand Down Expand Up @@ -118,7 +119,7 @@
"vite-plugin-node-polyfills": "^0.22.0"
},
"dependencies": {
"@permaweb/aoconnect": "^0.0.55",
"@permaweb/aoconnect": "^0.0.57",
"arbundles": "0.11.0",
"arweave": "1.14.4",
"axios": "1.7.2",
Expand Down
4 changes: 2 additions & 2 deletions src/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import {
spawn,
unmonitor,
} from '@permaweb/aoconnect';
import { ArconnectSigner, ArweaveSigner } from 'arbundles';
import { Signer } from 'arbundles';

import {
AllowedProtocols,
Expand All @@ -41,7 +41,7 @@ export type TransactionId = string;
export type ProcessId = string;

// TODO: append this with other configuration options (e.g. local vs. remote evaluation)
export type ContractSigner = ArweaveSigner | ArconnectSigner;
export type ContractSigner = Signer | Window['arweaveWallet'];
export type WithSigner<T = NonNullable<unknown>> = {
signer: ContractSigner;
} & T; // TODO: optionally allow JWK in place of signer
Expand Down
7 changes: 4 additions & 3 deletions src/common/ant.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,15 @@ import {
AoANTState,
AoANTWrite,
AoMessageResult,
ContractSigner,
AoSigner,
OptionalSigner,
ProcessConfiguration,
WalletAddress,
WithSigner,
isProcessConfiguration,
isProcessIdConfiguration,
} from '../types.js';
import { createAoSigner } from '../utils/ao.js';
import { AOProcess, InvalidContractConfigurationError } from './index.js';

export class ANT {
Expand Down Expand Up @@ -227,14 +228,14 @@ export class AoANTReadable implements AoANTRead {
}

export class AoANTWriteable extends AoANTReadable implements AoANTWrite {
private signer: ContractSigner;
private signer: AoSigner;

constructor({
signer,
...config
}: WithSigner<Required<ProcessConfiguration>>) {
super(config);
this.signer = signer;
this.signer = createAoSigner(signer);
}

/**
Expand Down
35 changes: 3 additions & 32 deletions src/common/contracts/ao-process.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,8 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import { connect } from '@permaweb/aoconnect';
import { createData } from 'arbundles';

import { AOContract, AoClient, ContractSigner } from '../../types.js';
import { AOContract, AoClient, AoSigner } from '../../types.js';
import { safeDecode } from '../../utils/json.js';
import { version } from '../../version.js';
import { WriteInteractionError } from '../error.js';
Expand All @@ -42,34 +41,6 @@ export class AOProcess implements AOContract {
this.ao = ao;
}

// TODO: could abstract into our own interface that constructs different signers
static async createAoSigner(
signer: ContractSigner,
): Promise<
(args: {
data: string | Buffer;
tags?: { name: string; value: string }[];
target?: string;
anchor?: string;
}) => Promise<{ id: string; raw: ArrayBuffer }>
> {
// ensure appropriate permissions are granted with injected signers.
if (signer.publicKey === undefined && 'setPublicKey' in signer) {
await signer.setPublicKey();
}

const aoSigner = async ({ data, tags, target, anchor }) => {
const dataItem = createData(data, signer, { tags, target, anchor });
const signedData = dataItem.sign(signer).then(async () => ({
id: await dataItem.id,
raw: await dataItem.getRaw(),
}));
return signedData;
};

return aoSigner;
}

async read<K>({
tags,
retries = 3,
Expand Down Expand Up @@ -137,7 +108,7 @@ export class AOProcess implements AOContract {
}: {
tags: Array<{ name: string; value: string }>;
data?: I;
signer: ContractSigner;
signer: AoSigner;
retries?: number;
}): Promise<{ id: string; result?: K }> {
// main purpose of retries is to handle network errors/new process delays
Expand All @@ -158,7 +129,7 @@ export class AOProcess implements AOContract {
// TODO: any other default tags we want to add?
tags: [...tags, { name: 'AR-IO-SDK', value: version }],
data: typeof data !== 'string' ? JSON.stringify(data) : data,
signer: await AOProcess.createAoSigner(signer),
signer,
});

this.logger.debug(`Sent message to process`, {
Expand Down
11 changes: 6 additions & 5 deletions src/common/io.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ import {
isProcessConfiguration,
isProcessIdConfiguration,
} from '../io.js';
import { mIOToken } from '../token.js';
import { AoSigner, mIOToken } from '../token.js';
import {
AoArNSNameDataWithName,
AoBalanceWithAddress,
Expand All @@ -52,6 +52,7 @@ import {
WithSigner,
WriteOptions,
} from '../types.js';
import { createAoSigner } from '../utils/ao.js';
import { defaultArweave } from './arweave.js';
import { AOProcess } from './contracts/ao-process.js';
import { InvalidContractConfigurationError } from './error.js';
Expand Down Expand Up @@ -578,7 +579,7 @@ export class IOReadable implements AoIORead {

export class IOWriteable extends IOReadable implements AoIOWrite {
protected declare process: AOProcess;
private signer: ContractSigner;
private signer: AoSigner;
constructor({
signer,
...config
Expand All @@ -594,17 +595,17 @@ export class IOWriteable extends IOReadable implements AoIOWrite {
processId: IO_TESTNET_PROCESS_ID,
}),
});
this.signer = signer;
this.signer = createAoSigner(signer);
} else if (isProcessConfiguration(config)) {
super({ process: config.process });
this.signer = signer;
this.signer = createAoSigner(signer);
} else if (isProcessIdConfiguration(config)) {
super({
process: new AOProcess({
processId: config.processId,
}),
});
this.signer = signer;
this.signer = createAoSigner(signer);
} else {
throw new InvalidContractConfigurationError();
}
Expand Down
5 changes: 2 additions & 3 deletions src/io.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,10 @@ import {
VaultData,
WeightedObserver,
} from './contract-state.js';
import { mIOToken } from './token.js';
import { AoSigner, mIOToken } from './token.js';
import {
AoMessageResult,
BlockHeight,
ContractSigner,
JoinNetworkParams,
ProcessId,
Timestamp,
Expand Down Expand Up @@ -111,7 +110,7 @@ export interface AOContract {
}: {
tags: { name: string; value: string }[];
data: I;
signer: ContractSigner;
signer: AoSigner;
}): Promise<{ id: string; result?: K }>;
}

Expand Down
7 changes: 7 additions & 0 deletions src/token.ts
Original file line number Diff line number Diff line change
Expand Up @@ -153,3 +153,10 @@ export class mIOToken extends PositiveFiniteInteger {
return new IOToken(this.valueOf() / MIO_PER_IO);
}
}

export type AoSigner = (args: {
data: string | Buffer;
tags?: { name: string; value: string }[];
target?: string;
anchor?: string;
}) => Promise<{ id: string; raw: ArrayBuffer }>;
36 changes: 31 additions & 5 deletions src/utils/ao.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import { connect } from '@permaweb/aoconnect';
import { connect, createDataItemSigner } from '@permaweb/aoconnect';
import { createData } from 'arbundles';

import { defaultArweave } from '../common/arweave.js';
import { AOProcess } from '../common/index.js';
Expand All @@ -24,7 +25,7 @@ import {
DEFAULT_SCHEDULER_ID,
} from '../constants.js';
import { ANTState } from '../contract-state.js';
import { AoClient, ContractSigner } from '../types.js';
import { AoClient, AoSigner, ContractSigner } from '../types.js';

export async function spawnANT({
signer,
Expand All @@ -35,7 +36,7 @@ export async function spawnANT({
state,
stateContractTxId,
}: {
signer: ContractSigner;
signer: AoSigner;
module?: string;
luaCodeTxId?: string;
ao?: AoClient;
Expand All @@ -52,7 +53,7 @@ export async function spawnANT({
const processId = await ao.spawn({
module,
scheduler,
signer: await AOProcess.createAoSigner(signer),
signer,
});

const aosClient = new AOProcess({
Expand Down Expand Up @@ -92,7 +93,7 @@ export async function evolveANT({
luaCodeTxId = ANT_LUA_ID,
ao = connect(),
}: {
signer: ContractSigner;
signer: AoSigner;
processId: string;
luaCodeTxId?: string;
ao?: AoClient;
Expand Down Expand Up @@ -120,3 +121,28 @@ export async function evolveANT({

return id;
}

export function createAoSigner(signer: ContractSigner): AoSigner {
if (!('publicKey' in signer)) {
return createDataItemSigner(signer) as any;

Check warning on line 127 in src/utils/ao.ts

View workflow job for this annotation

GitHub Actions / build / build (18.x, lint)

Unexpected any. Specify a different type

Check warning on line 127 in src/utils/ao.ts

View workflow job for this annotation

GitHub Actions / build (18.x, lint)

Unexpected any. Specify a different type

Check warning on line 127 in src/utils/ao.ts

View workflow job for this annotation

GitHub Actions / build / build (20.x, lint)

Unexpected any. Specify a different type

Check warning on line 127 in src/utils/ao.ts

View workflow job for this annotation

GitHub Actions / build (20.x, lint)

Unexpected any. Specify a different type
}

const aoSigner = async ({ data, tags, target, anchor }) => {
// ensure appropriate permissions are granted with injected signers.
if (
signer.publicKey === undefined &&
'setPublicKey' in signer &&
typeof signer.setPublicKey === 'function'
) {
await signer.setPublicKey();
}
const dataItem = createData(data, signer, { tags, target, anchor });
const signedData = dataItem.sign(signer).then(async () => ({
id: await dataItem.id,
raw: await dataItem.getRaw(),
}));
return signedData;
};

return aoSigner;
}
Loading

0 comments on commit 8497a20

Please sign in to comment.