Skip to content

Commit

Permalink
feat(web-components): allow custom signer instead of keplr
Browse files Browse the repository at this point in the history
  • Loading branch information
samsiegart committed Feb 20, 2024
1 parent 7a70e1e commit 2d4c3bc
Show file tree
Hide file tree
Showing 8 changed files with 337 additions and 276 deletions.
4 changes: 4 additions & 0 deletions packages/web-components/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

export { makeAgoricKeplrConnection } from './src/keplr-connection/KeplrConnection.js';
export { makeAgoricWalletConnection } from './src/wallet-connection/walletConnection.js';
export {
agoricRegistryTypes,
agoricConverters,
} from './src/wallet-connection/signerOptions.js';
export { suggestChain } from './src/wallet-connection/suggestChain.js';
export { Errors as AgoricKeplrConnectionErrors } from './src/errors.js';
export {
Expand Down
1 change: 1 addition & 0 deletions packages/web-components/src/wallet-connection/chainInfo.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
// @ts-check
/** @typedef {import('@keplr-wallet/types').Bech32Config} Bech32Config */
/** @typedef {import('@keplr-wallet/types').FeeCurrency} FeeCurrency */

Expand Down
61 changes: 61 additions & 0 deletions packages/web-components/src/wallet-connection/connectKeplr.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
// @ts-check
import { Registry } from '@cosmjs/proto-signing';
import {
SigningStargateClient,
AminoTypes,
defaultRegistryTypes,
createBankAminoConverters,
createAuthzAminoConverters,
} from '@cosmjs/stargate';
import { Errors } from '../errors.js';
import { agoricConverters, agoricRegistryTypes } from './signerOptions.js';

/** @typedef {import('@keplr-wallet/types').Keplr} Keplr */

/**
*
* @param {string} chainId
* @param {string} rpc
*/
export const connectKeplr = async (chainId, rpc) => {
if (!('keplr' in window)) {
throw Error(Errors.noKeplr);
}
/** @type {import('@keplr-wallet/types').Keplr} */
// @ts-expect-error cast (checked above)
const keplr = window.keplr;

await null;
try {
await keplr.enable(chainId);
} catch {
throw Error(Errors.enableKeplr);
}

// Until we have SIGN_MODE_TEXTUAL,
// Use Amino because Direct results in ugly protobuf in the keplr UI.
const offlineSigner = await keplr.getOfflineSignerOnlyAmino(chainId);
console.debug('InteractiveSigner', { offlineSigner });

// Currently, Keplr extension manages only one address/public key pair.
const [account] = await offlineSigner.getAccounts();
const { address } = account;

const signingClient = await SigningStargateClient.connectWithSigner(
rpc,
offlineSigner,
{
aminoTypes: new AminoTypes({
...agoricConverters,
...createBankAminoConverters(),
...createAuthzAminoConverters(),
}),
registry: new Registry([...defaultRegistryTypes, ...agoricRegistryTypes]),
},
);

return {
address,
client: signingClient,
};
};
123 changes: 123 additions & 0 deletions packages/web-components/src/wallet-connection/makeAgoricSigner.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
// @ts-check
import { fromBech32, toBase64 } from '@cosmjs/encoding';
import { assertIsDeliverTxSuccess } from '@cosmjs/stargate';
import { stableCurrency } from './chainInfo.js';
import { AgoricMsgs } from './signerOptions.js';

/** @typedef {import("@cosmjs/proto-signing").EncodeObject} EncodeObject */
/** @typedef {import("@cosmjs/stargate").AminoConverters} AminoConverters */
/** @typedef {import("@cosmjs/stargate").StdFee} StdFee */
/** @typedef {import('@keplr-wallet/types').ChainInfo} ChainInfo */
/** @typedef {import('@keplr-wallet/types').Keplr} Keplr */
/** @typedef {import('@cosmjs/stargate').SigningStargateClient} SigningStargateClient */

/**
* @param {string} address
* @returns {Uint8Array}
*/
const toAccAddress = address => {
return fromBech32(address).data;
};

// XXX domain of @agoric/cosmic-proto
/**
* non-exhaustive list of powerFlags
*
* See also MsgProvision in golang/cosmos/proto/agoric/swingset/msgs.proto
*/
const PowerFlags = {
SMART_WALLET: 'SMART_WALLET',
};

/** @typedef {{owner: string, spendAction: string}} WalletSpendAction */

/**
* @returns {StdFee}
*/
const zeroFee = () => {
const { coinMinimalDenom: denom } = stableCurrency;
const fee = {
amount: [{ amount: '0', denom }],
gas: '300000', // TODO: estimate gas?
};
return fee;
};

/**
* Use a signing client to
* @param {SigningStargateClient} signingClient
* @param {string} address
* Ref: https://docs.keplr.app/api/
*/
export const makeAgoricSigner = (signingClient, address) => {
const fee = zeroFee();

return harden({
/**
* Sign and broadcast Provision for a new smart wallet
*
* @throws if account does not exist on chain, user cancels,
* RPC connection fails, RPC service fails to broadcast (
* for example, if signature verification fails)
*/
provisionSmartWallet: async () => {
const { accountNumber, sequence } = await signingClient.getSequence(
address,
);
console.log({ accountNumber, sequence });

const b64address = toBase64(toAccAddress(address));

const act1 = {
typeUrl: AgoricMsgs.MsgProvision.typeUrl,
value: {
address: b64address,
nickname: 'my wallet',
powerFlags: [PowerFlags.SMART_WALLET],
submitter: b64address,
},
};

const msgs = [act1];
console.log('sign provision', { address, msgs, fee });

const tx = await signingClient.signAndBroadcast(address, msgs, fee);
console.log('spend action result tx', tx);
assertIsDeliverTxSuccess(tx);

return tx;
},

/**
* Sign and broadcast WalletSpendAction
*
* @param {string} spendAction marshaled offer
* @throws if account does not exist on chain, user cancels,
* RPC connection fails, RPC service fails to broadcast (
* for example, if signature verification fails)
*/
submitSpendAction: async spendAction => {
const { accountNumber, sequence } = await signingClient.getSequence(
address,
);
console.debug({ accountNumber, sequence });

const act1 = {
typeUrl: AgoricMsgs.MsgWalletSpendAction.typeUrl,
value: {
owner: toBase64(toAccAddress(address)),
spendAction,
},
};

const msgs = [act1];
console.debug('sign spend action', { address, msgs, fee });

const tx = await signingClient.signAndBroadcast(address, msgs, fee);
console.debug('spend action result tx', tx);
assertIsDeliverTxSuccess(tx);

return tx;
},
});
};
Loading

0 comments on commit 2d4c3bc

Please sign in to comment.