Skip to content
This repository has been archived by the owner on Feb 23, 2023. It is now read-only.

feat: Enable address converting for deployment args #79

Merged
merged 11 commits into from
Dec 27, 2021
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,13 +39,13 @@ the polyjuice-provider convert the two different address type automatically for

short version:

- you need to take care of the address converting by yourself when deploying contract with constructor arguments which contains address type.
- ~~you need to take care of the address converting by yourself when deploying contract with constructor arguments which contains address type.~~ For web3.js and ethers, we provide one helper function to convert deployment arguments to save some works for you, checkout [how to use it](docs/get-started.md#example-deploy-contract).
- do not use polyjuice-provider with contract address which has not been created on-chain.
- do not use polyjuice-provider to transfer ether.

long version and why:

- currently we do **NOT** support address-converting [for contract deployment arguments](packages/ethers/tests/deployArgs.test.ts#L77), that is to say, when you use ethers/web3.js/truffle to deploy contract with address as constructor arguments, you should take care of the address converting by yourself to pass the right polyjuiceAddress instead of ethAddress. however, this limits might be removed in the future for new release version. please stay alert.
- ~~currently we do **NOT** support address-converting [for contract deployment arguments](packages/ethers/tests/deployArgs.test.ts#L77), that is to say, when you use ethers/web3.js/truffle to deploy contract with address as constructor arguments, you should take care of the address converting by yourself to pass the right polyjuiceAddress instead of ethAddress. however, this limits might be removed in the future for new release version. please stay alert.~~ For web3.js and ethers, Converting address for deployment arguments are supported now, you need to [call this function](docs/get-started.md#example-deploy-contract) to convert it explicitly before deploy your contract.
- currently we do **NOT** support passing contract-address which has not been created yet on chain as address parameter in tx's [data field](https://ethereum.org/en/developers/docs/transactions/) to interact with smart-contract. noticed that, this doesn't mean we do not support `create2`. you can use create2 whenever you want. but if it has not been created on chain, you can not use this address as parameter to feed other contracts. the address-converting will go wrong. as soon as the contract been created, there is no limit.
- currently we are **NOT** supporting transfer ether to another EOA address directly via provider for safety reason, so if you are sending a transfer transaction through polyjuice-provider, it won't work. however, you can still construct a transaction which interact with ERC20-contract and tell the contract to transfer token for you.

Expand Down
19 changes: 16 additions & 3 deletions docs/get-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -143,9 +143,15 @@ const implementationFactory = new ContractFactory(
contract.bytecode,
deployer,
);
const tx = implementationFactory.getDeployTransaction(
'<your deploy arguments> Note: if the arguments contains address type, you need to provide polyjuiceAddress instead of ethAddress, here provider can not do address-converting for you'

const deployArgs = ['<your contract constructor args>'];
// you need the following step to convert the deployArgs before deploying contract:
const newDeployArgs = await deployer.convertDeployArgs(
deployArgs,
contract.abi as AbiItems,
contract.bytecode
);
const tx = implementationFactory.getDeployTransaction(...newDeployArgs);
tx.gasPrice = 0;
tx.gasLimit = 1_000_000;
deployer.sendTransaction(tx);
Expand Down Expand Up @@ -384,10 +390,17 @@ web3.eth.Contract._accounts = web3.eth.accounts;
web3.eth.accounts.wallet.add(`your private key`);

const myContract = await web3.eth.Contract(`your contract's abi`);
const deployArgs = ['your contract constructor args'];
// you need the following step to convert the deployArgs before deploying contract:
const newDeployArgs = await polyjuiceAccounts.convertDeployArgs(
deployArgs,
'contract abi items',
'contract binary'
);
const contractInstance = myContract
.deploy({
data: `contract bin`,
arguments: ['<your deploy arguments> Note: if the arguments contains address type, you need to provide polyjuiceAddress instead of ethAddress, here provider can not do address-converting for you'],
arguments: newDeployArgs,
})
.send({
gas: "0x30d40",
Expand Down
4 changes: 2 additions & 2 deletions docs/known-caveats.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,15 @@ the polyjuice-provider convert the two different address type automatically for

## short version

- you need to take care of the address converting by yourself when deploying contract with constructor arguments which contains address type.
- ~~you need to take care of the address converting by yourself when deploying contract with constructor arguments which contains address type.~~ For web3.js and ethers, we provide one helper function to convert deployment arguments to save some works for you, checkout [how to use it](docs/get-started.md#example-deploy-contract).

- do not use polyjuice-provider with contract address which has not been created on-chain.

- do not use polyjuice-provider to transfer ether.

## long version and why

- currently we do NOT support address-converting for contract deployment arguments, that is to say, when you use ethers/web3.js/truffle to deploy contract with address as constructor arguments, you should take care of the address converting by yourself to pass the right polyjuiceAddress instead of ethAddress. however, this limits might be removed in the future for new release version. please stay alert.
- ~~currently we do **NOT** support address-converting [for contract deployment arguments](packages/ethers/tests/deployArgs.test.ts#L77), that is to say, when you use ethers/web3.js/truffle to deploy contract with address as constructor arguments, you should take care of the address converting by yourself to pass the right polyjuiceAddress instead of ethAddress. however, this limits might be removed in the future for new release version. please stay alert.~~ For web3.js and ethers, Converting address for deployment arguments are supported now, you need to [call this function](docs/get-started.md#example-deploy-contract) to convert it explicitly before deploy your contract.

- currently we do NOT support passing contract-address which has not been created yet on chain as address parameter in tx's data field to interact with smart-contract. noticed that, this doesn't mean we do not support create2. you can use create2 whenever you want. but if it has not been created on chain, you can not use this address as parameter to feed other contracts. the address-converting will go wrong. as soon as the contract been created, there is no limit.

Expand Down
9 changes: 9 additions & 0 deletions packages/base/src/constant.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,12 @@ export const WAIT_LOOP_INTERVAL_MILSECS = 1000; // 1 secs
export const DEFAULT_SUDT_ID_HEX_STRING = "0x1";
export const DEFAULT_SUDT_FEE_HEX_STRING = "0x0";
export const DEFAULT_ETH_TO_CKB_SUDT_DECIMAL = 100_0000_0000; // 1 eth = 1 ckb in layer2

// contract deployment
export const MAX_CONTRACT_CODE_SIZE_IN_BYTE = 24000; // 24k byte
export const CONTRACT_BYTE_CODE_ID_OFFSET = 12; // we use last 12 of bytecode string to identified contract
export const CONTRACT_BYTE_CODE_HASH_HEAD_IN_BYTE = 8; // keccak(bytecode) first 8 bytes as HASH_HEAD

// docs link
export const DEPLOY_CONTRACT_DOCS_LINK =
"https://github.com/nervosnetwork/polyjuice-provider/readme.md";
96 changes: 95 additions & 1 deletion packages/base/src/process.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,18 @@ import {
buildL2TransactionWithAddressMapping,
buildRawL2TransactionWithAddressMapping,
EthTransaction,
formalizeEthToAddress,
Godwoker,
normalizeEthTransaction,
splitByteCodeAndConstructorArgs,
DeploymentRecords,
} from "./util";
import { serializeAbiItem } from "./abi";
import { SigningMessageType } from "./types";
import { EMPTY_ABI_ITEM_SERIALIZE_STR } from "./constant";
import {
EMPTY_ABI_ITEM_SERIALIZE_STR,
DEFAULT_EMPTY_ETH_ADDRESS,
} from "./constant";
import _ from "lodash";

export type SerializeSignedTransactionString = HexString;
Expand Down Expand Up @@ -85,6 +91,94 @@ export async function buildEstimateGasTransaction(
return rawL2Tx;
}

// one special type transaction: contract deployment.
// to address must be zero address and must be a send transaction
// we can't convert address for contract deployment since:
// (1. it doesn't have a function signature in eth_tx's input data
// (2. we support multiple contract ABIs
// thus it is impossible to identified a deploy transaction's abiItem and do the address converting
// instead, we ask developer to explicitly convert the address while we recorded some info to build address mapping if needed.
export async function buildDeployProcess(
deployAddressMapping: AddressMappingItem[],
deploymentRecords: DeploymentRecords,
abi: Abi,
godwoker: Godwoker,
tx: EthTransaction,
signingMethod: any,
signingMessageType_?: SigningMessageType
): Promise<SerializeSignedTransactionString> {
const process: Process = {
type: ProcessTransactionType.send,
signingMethod: signingMethod,
signingMessageType: signingMessageType_,
};
if (formalizeEthToAddress(tx.to) !== DEFAULT_EMPTY_ETH_ADDRESS) {
throw new Error(`deployTransaction must be a zero to address transaction`);
}
if (!tx.from) {
throw new Error("tx.from can not be missing in sendTransaction!");
}
if (!process.signingMethod) {
throw new Error(
"process.signingMethod can not be missing in deployTransaction!"
);
}

const t = normalizeEthTransaction({
from: tx.from,
to: tx.to,
value: tx.value,
gas: tx.gas,
gasPrice: tx.gasPrice,
data: tx.data,
});

const rawL2Tx = await godwoker.assembleRawL2Transaction(t);

const signingMessageType =
process.signingMessageType || SigningMessageType.withPrefix;

// generate message to sign
const senderScriptHash = godwoker.computeScriptHashByEoaEthAddress(t.from);
const receiverScriptHash = await godwoker.getScriptHashByAccountId(
parseInt(rawL2Tx.to_id, 16)
);
const message = godwoker.generateTransactionMessageToSign(
rawL2Tx,
senderScriptHash,
receiverScriptHash,
signingMessageType === SigningMessageType.withPrefix
);

const _signature = await process.signingMethod!(message);
const signature = godwoker.packSignature(_signature);
const l2Tx = { raw: rawL2Tx, signature: signature };

const result = splitByteCodeAndConstructorArgs(tx.data, deploymentRecords);
if (result == null) {
// let's build a standard send transaction
// since we can't find matched deploymentRecords
return buildSendTransaction(
abi,
godwoker,
tx,
signingMethod,
signingMessageType_
);
}

const abiItem = deploymentRecords[result.signature];
const _abiItem = _.cloneDeep(abiItem); // do not change the original abi object
let serializedAbiItem = serializeAbiItem(_abiItem);

const polyL2Tx = buildL2TransactionWithAddressMapping(
l2Tx,
deployAddressMapping,
serializedAbiItem
);
return godwoker.serializeL2TransactionWithAddressMapping(polyL2Tx);
}

export async function buildProcess(
abi: Abi,
godwoker: Godwoker,
Expand Down
Loading