Skip to content

Commit

Permalink
Align and document interface changes
Browse files Browse the repository at this point in the history
  • Loading branch information
cag committed Jan 7, 2020
1 parent 8fc8c30 commit 1dcbfac
Show file tree
Hide file tree
Showing 3 changed files with 69 additions and 23 deletions.
44 changes: 42 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,14 @@ const cpk = await CPK.create({

Please refer to the `migrations/` folder of this package for information on how to deploy the required contracts on a network, and note that these addresses must be available for the connected network in order for *CPK* creation to be successful.

### CPK#getOwnerAccount

This may be used to figure out which account the proxy considers the owner account. It returns a Promise which resolves to the owner account:

```js
const ownerAccount = await cpk.getOwnerAccount()
```

### CPK#address

Once created, the `address` property on a *CPK* instance will provide the proxy's checksummed Ethereum address:
Expand All @@ -91,6 +99,17 @@ Once created, the `address` property on a *CPK* instance will provide the proxy'

This address is calculated even if the proxy has not been deployed yet, and it is deterministically generated from the proxy owner address.

#### Support for WalletConnected Gnosis Safe

If the provider underlying the *CPK* instance is connected to a Gnosis Safe via WalletConnect, the address will match the owner account:

```js
const ownerAccount = await cpk.getOwnerAccount()
cpk.address === ownerAccount // this will be true in that case
```

*CPK* will use the Safe's native support for batching transactions, and will not create an additional proxy contract account.

### CPK#execTransactions

To execute transactions using a *CPK* instance, call `execTransactions` with an *Array* of transactions to execute. If the proxy has not been deployed, this will also batch the proxy's deployment into the transaction. Multiple transactions will be batched and executed together if the proxy has been deployed.
Expand Down Expand Up @@ -155,7 +174,7 @@ const { promiEvent, receipt } = await cpk.execTransactions([
Suppose instead `erc20` and `exchange` are Truffle contract abstraction instances instead. Since Truffle contract abstraction instances contain a reference to an underlying *web3.eth.Contract* instance, they may be used in a similar manner:

```js
const { promiEvent, receipt } = await cpk.execTransactions([
const { promiEvent, hash } = await cpk.execTransactions([
{
operation: CPK.CALL,
to: erc20.address,
Expand Down Expand Up @@ -183,7 +202,7 @@ const { promiEvent, receipt } = await cpk.execTransactions([
Similarly to the example in the previous section, suppose that `erc20` is a *ethers.Contract* instance for an ERC20 token for which the proxy account holds a balance, and `exchange` is a *ethers.Contract* instance of an exchange contract with an deposit requirement, where calling the deposit function on the exchange requires an allowance for the exchange by the depositor. Batching these transactions may be done like so:

```js
const { transactionResponse, transactionReceipt } = await cpk.execTransactions([
const { transactionResponse, hash } = await cpk.execTransactions([
{
operation: CPK.CALL,
to: erc20.address,
Expand Down Expand Up @@ -228,3 +247,24 @@ const txObject = await cpk.execTransactions(
{ gasPrice: `${3e9}` },
);
```

#### Support for WalletConnected Gnosis Safe

When WalletConnected to Gnosis Safe, `execTransactions` will use the Safe's native support for sending batch transactions (via `gs_multi_send`). In this case, the gas price option is not available, and `execTransactions` will only return a transaction hash.

```js
const { hash } = await cpk.execTransactions([
{
operation: CPK.CALL,
to: '0x90f8bf6a479f320ead074411a4b0e7944ea8c9c1',
value: `${1e18}`,
data: '0x',
},
{
operation: CPK.CALL,
to: '0xffcf8fdee72ac11b5c542428b35eef5769c409f0',
value: `${1e18}`,
data: '0x',
},
]);
```
21 changes: 11 additions & 10 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -310,9 +310,9 @@ const CPK = class CPK {
...(options || {}),
};
const promiEventToPromise = (promiEvent) => new Promise(
(resolve, reject) => promiEvent.on(
'confirmation',
(confirmationNumber, receipt) => resolve({ sendOptions, promiEvent, receipt }),
(resolve, reject) => promiEvent.once(
'transactionHash',
(hash) => resolve({ sendOptions, promiEvent, hash }),
).catch(reject),
);

Expand Down Expand Up @@ -341,7 +341,7 @@ const CPK = class CPK {
};

attemptSafeProviderMultiSendTxs = async (txs) => {
const res = await (
const hash = await (
this.web3.currentProvider.host === 'CustomProvider'
? this.web3.currentProvider.send('gs_multi_send', txs)
: new Promise(
Expand All @@ -357,7 +357,7 @@ const CPK = class CPK {
}),
)
);
return res;
return { hash };
};

codeAtAddress = await this.web3.eth.getCode(this.address);
Expand Down Expand Up @@ -387,20 +387,21 @@ const CPK = class CPK {
...params,
...(options == null ? [] : [options]),
);
const transactionReceipt = await transactionResponse.wait();
return { transactionResponse, transactionReceipt };
return { transactionResponse, hash: transactionResponse.hash };
};

attemptSafeProviderSendTx = async (txObj) => {
const transactionResponse = await this.signer.sendTransaction({
...txObj,
...(options || {}),
});
const transactionReceipt = await transactionResponse.wait();
return { transactionResponse, transactionReceipt };
return { transactionResponse, hash: transactionResponse.hash };
};

attemptSafeProviderMultiSendTxs = (txs) => this.signer.provider.send('gs_multi_send', txs);
attemptSafeProviderMultiSendTxs = async (txs) => {
const hash = await this.signer.provider.send('gs_multi_send', txs);
return { hash };
};

codeAtAddress = (await this.signer.provider.getCode(this.address));

Expand Down
27 changes: 16 additions & 11 deletions test/contract-proxy-kit.js
Original file line number Diff line number Diff line change
Expand Up @@ -280,7 +280,7 @@ function shouldSupportDifferentTransactions({
}],
{ gasPrice },
);
const gasUsed = getGasUsed(txObj);
const gasUsed = await getGasUsed(txObj);

const endingBalance = await getBalance(executor || ownerAccount);
const gasCosts = startingBalance.sub(endingBalance).toNumber();
Expand Down Expand Up @@ -313,7 +313,7 @@ function shouldSupportDifferentTransactions({
],
{ gasPrice },
);
const gasUsed = getGasUsed(txObj);
const gasUsed = await getGasUsed(txObj);

const endingBalance = await getBalance(executor || ownerAccount);
const gasCosts = startingBalance.sub(endingBalance).toNumber();
Expand All @@ -334,14 +334,17 @@ function shouldWorkWithWeb3(Web3, defaultAccount, safeOwner, gnosisSafeProviderB
randomHexWord: () => web3Box[0].utils.randomHex(32),
fromWei: (amount) => Number(web3Box[0].utils.fromWei(amount)),
getTransactionCount: (account) => web3Box[0].eth.getTransactionCount(account),
testedTxObjProps: 'the PromiEvent for the transaction and the receipt',
testedTxObjProps: 'the PromiEvent for the transaction and the hash',
getBalance: (address) => web3Box[0].eth.getBalance(address)
.then((balance) => web3Box[0].utils.toBN(balance)),
getGasUsed: ({ receipt }) => receipt.gasUsed,

checkTxObj: ({ promiEvent, receipt }) => {
getGasUsed: ({ promiEvent }) => new Promise(
(resolve, reject) => promiEvent
.on('confirmation', (confNumber, receipt) => resolve(receipt.gasUsed))
.on('error', reject),
),
checkTxObj: ({ promiEvent, hash }) => {
should.exist(promiEvent);
should.exist(receipt);
should.exist(hash);
},
});

Expand Down Expand Up @@ -453,11 +456,13 @@ function shouldWorkWithEthers(ethers, defaultAccount, safeOwner, gnosisSafeProvi
fromWei: (amount) => Number(ethers.utils.formatUnits(amount.toString(), 'ether')),
getTransactionCount: signer.provider.getTransactionCount.bind(signer.provider),
getBalance: signer.provider.getBalance.bind(signer.provider),
getGasUsed: ({ transactionReceipt }) => transactionReceipt.gasUsed.toNumber(),
testedTxObjProps: 'the TransactionResponse and the TransactionReceipt',
checkTxObj: ({ transactionResponse, transactionReceipt }) => {
getGasUsed: async ({ transactionResponse }) => (
await transactionResponse.wait()
).gasUsed.toNumber(),
testedTxObjProps: 'the TransactionResponse and the hash',
checkTxObj: ({ transactionResponse, hash }) => {
should.exist(transactionResponse);
should.exist(transactionReceipt);
should.exist(hash);
},
});

Expand Down

0 comments on commit 1dcbfac

Please sign in to comment.