Skip to content

Commit

Permalink
414 contract deployment facilitate usage (#497)
Browse files Browse the repository at this point in the history
* feat: adding contract factory

* refactor: removing previous deploy contract function

* test: adding ERC721 contract factory

* refactor: moving empty string check in regex

* docs: updating contract examples

* refactor: adding address in contract constructor

* refactor: updating numeric regex

* docs: adding contract comments

* test: failed contract deployment

* test: wait for a contract deployment not started

* refactor: making abi public in contract

* docs: setting contract factory to a const in the examples

---------

Co-authored-by: Rodolfo Pietro Calabrò <[email protected]>
  • Loading branch information
Valazan and rodolfopietro97 authored Jan 31, 2024
1 parent 9656c22 commit b7d60dd
Show file tree
Hide file tree
Showing 15 changed files with 451 additions and 214 deletions.
38 changes: 22 additions & 16 deletions docs/contracts.md
Original file line number Diff line number Diff line change
Expand Up @@ -159,16 +159,21 @@ const _soloUrl = 'http://localhost:8669/';
const soloNetwork = new HttpClient(_soloUrl);
const thorSoloClient = new ThorClient(soloNetwork);

// Deploying the ERC20 contract using the Thor client and the deployer's private key
const transaction = await thorSoloClient.contracts.deployContract(
privateKeyDeployer,
erc20ContractBytecode
// Creating the contract factory
const contractFactory = thorSoloClient.contracts.createContractFactory(
VIP180_ABI,
erc20ContractBytecode,
privateKeyDeployer
);

// Deploying the contract
await contractFactory.startDeployment();

// Awaiting the contract deployment
const contract = await contractFactory.waitForDeployment();

// Awaiting the transaction receipt to confirm successful contract deployment
const receipt = await thorSoloClient.transactions.waitForTransaction(
transaction.id
);
const receipt = contract.deployTransactionReceipt;

// Asserting that the contract deployment didn't revert, indicating a successful deployment
expect(receipt.reverted).toEqual(false);
Expand Down Expand Up @@ -217,18 +222,19 @@ const thorSoloClient = new ThorClient(soloNetwork);

// Defining a function for deploying the ERC20 contract
const setupERC20Contract = async (): Promise<string> => {
// Deploying the ERC20 contract using the Thor client and the deployer's private key
const transaction = await thorSoloClient.contracts.deployContract(
privateKeyDeployer,
erc20ContractBytecode
const contractFactory = thorSoloClient.contracts.createContractFactory(
VIP180_ABI,
erc20ContractBytecode,
privateKeyDeployer
);

// Awaiting the transaction receipt to confirm successful contract deployment
const receipt = await thorSoloClient.transactions.waitForTransaction(
transaction.id
);
// Deploying the contract
await contractFactory.startDeployment();

// Waiting for the contract to be deployed
const contract = await contractFactory.waitForDeployment();

return receipt.outputs[0].contractAddress;
return contract.address;
};

// Setting up the ERC20 contract and getting its address
Expand Down
19 changes: 12 additions & 7 deletions docs/examples/contracts/contract-create-ERC20-token.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,21 @@ const _soloUrl = 'http://localhost:8669/';
const soloNetwork = new HttpClient(_soloUrl);
const thorSoloClient = new ThorClient(soloNetwork);

// Deploying the ERC20 contract using the Thor client and the deployer's private key
const transaction = await thorSoloClient.contracts.deployContract(
privateKeyDeployer,
erc20ContractBytecode
// Creating the contract factory
const contractFactory = thorSoloClient.contracts.createContractFactory(
VIP180_ABI,
erc20ContractBytecode,
privateKeyDeployer
);

// Deploying the contract
await contractFactory.startDeployment();

// Awaiting the contract deployment
const contract = await contractFactory.waitForDeployment();

// Awaiting the transaction receipt to confirm successful contract deployment
const receipt = await thorSoloClient.transactions.waitForTransaction(
transaction.id
);
const receipt = contract.deployTransactionReceipt;

// Asserting that the contract deployment didn't revert, indicating a successful deployment
expect(receipt.reverted).toEqual(false);
Expand Down
19 changes: 10 additions & 9 deletions docs/examples/contracts/contract-transfer-ERC20-token.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,18 +20,19 @@ const thorSoloClient = new ThorClient(soloNetwork);

// Defining a function for deploying the ERC20 contract
const setupERC20Contract = async (): Promise<string> => {
// Deploying the ERC20 contract using the Thor client and the deployer's private key
const transaction = await thorSoloClient.contracts.deployContract(
privateKeyDeployer,
erc20ContractBytecode
const contractFactory = thorSoloClient.contracts.createContractFactory(
VIP180_ABI,
erc20ContractBytecode,
privateKeyDeployer
);

// Awaiting the transaction receipt to confirm successful contract deployment
const receipt = await thorSoloClient.transactions.waitForTransaction(
transaction.id
);
// Deploying the contract
await contractFactory.startDeployment();

// Waiting for the contract to be deployed
const contract = await contractFactory.waitForDeployment();

return receipt.outputs[0].contractAddress;
return contract.address;
};

// Setting up the ERC20 contract and getting its address
Expand Down
42 changes: 36 additions & 6 deletions docs/examples/thor-client/contract.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { HttpClient, ThorClient } from '@vechain/vechain-sdk-network';
import { expect } from 'expect';
import type { DeployParams, InterfaceAbi } from '@vechain/vechain-sdk-core';

// 1 - Create thor client for solo network

Expand All @@ -13,14 +14,43 @@ const privateKeyDeployer =
'706e6acd567fdc22db54aead12cb39db01c4832f149f95299aa8dd8bef7d28ff'; // Private key of a test account with VTHO (energy) to pay for the deployment

const contractBytecode: string =
'0x608060405234801561001057600080fd5b50610150806100206000396000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c806360fe47b11461003b5780636d4ce63c14610057575b600080fd5b610055600480360381019061005091906100c3565b610075565b005b61005f61007f565b60405161006c91906100ff565b60405180910390f35b8060008190555050565b60008054905090565b600080fd5b6000819050919050565b6100a08161008d565b81146100ab57600080fd5b50565b6000813590506100bd81610097565b92915050565b6000602082840312156100d9576100d8610088565b5b60006100e7848285016100ae565b91505092915050565b6100f98161008d565b82525050565b600060208201905061011460008301846100f0565b9291505056fea26469706673582212206fd02e4453276839e38700c049f3c14a66636c948f1c2466388da61fb7574f3164736f6c63430008170033';

const result = await thorSoloClient.contracts.deployContract(
privateKeyDeployer,
contractBytecode
'0x608060405234801561001057600080fd5b506040516102063803806102068339818101604052810190610032919061007a565b80600081905550506100a7565b600080fd5b6000819050919050565b61005781610044565b811461006257600080fd5b50565b6000815190506100748161004e565b92915050565b6000602082840312156100905761008f61003f565b5b600061009e84828501610065565b91505092915050565b610150806100b66000396000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c806360fe47b11461003b5780636d4ce63c14610057575b600080fd5b610055600480360381019061005091906100c3565b610075565b005b61005f61007f565b60405161006c91906100ff565b60405180910390f35b8060008190555050565b60008054905090565b600080fd5b6000819050919050565b6100a08161008d565b81146100ab57600080fd5b50565b6000813590506100bd81610097565b92915050565b6000602082840312156100d9576100d8610088565b5b60006100e7848285016100ae565b91505092915050565b6100f98161008d565b82525050565b600060208201905061011460008301846100f0565b9291505056fea2646970667358221220785262acbf50fa50a7b4dc8d8087ca8904c7e6b847a13674503fdcbac903b67e64736f6c63430008170033';

const deployedContractAbi: InterfaceAbi = [
{
inputs: [],
name: 'get',
outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }],
stateMutability: 'view',
type: 'function'
},
{
inputs: [{ internalType: 'uint256', name: 'x', type: 'uint256' }],
name: 'set',
outputs: [],
stateMutability: 'nonpayable',
type: 'function'
}
];

// Creating the contract factory
let contractFactory = thorSoloClient.contracts.createContractFactory(
deployedContractAbi,
contractBytecode,
privateKeyDeployer
);

const receipt = await thorSoloClient.transactions.waitForTransaction(result.id);
// Deploy parameters to be used for the contract creation
const deployParams: DeployParams = { types: ['uint'], values: ['100'] };

// Deploying the contract
contractFactory = await contractFactory.startDeployment(deployParams);

// Awaiting the contract deployment
const contract = await contractFactory.waitForDeployment();

// Awaiting the transaction receipt to confirm successful contract deployment
const receipt = contract.deployTransactionReceipt;

expect(receipt.reverted).toEqual(false);
expect(receipt.outputs[0].contractAddress).toBeDefined();
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/utils/const/data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ const DECIMAL_INTEGER_REGEX = /^\d+$/;
* Allows optional "-" prefix and validates both integer and floating point numbers.
* Also allows for numbers with no leading digits (i.e. ".123", which is equivalent to "0.123").
*/
const NUMERIC_REGEX = /^-?\d*(\.\d+)?$/;
const NUMERIC_REGEX = /(^-?\d+(\.\d+)?)$|(^-?\.\d+)$/;

/**
* Default length of thor id hex string.
Expand Down
3 changes: 0 additions & 3 deletions packages/core/src/utils/data/data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -115,9 +115,6 @@ const removePrefix = (hex: string): string => {
* @returns - A boolean indicating whether the input is a valid numeric string.
*/
const isNumeric = (value: string): boolean => {
if (value === '') {
return false;
}
return NUMERIC_REGEX.test(value);
};

Expand Down
19 changes: 19 additions & 0 deletions packages/errors/src/model/core/contract.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { type DefaultErrorData } from '../../types';
import { ErrorBase } from '../base';

/**
* Error to be thrown when the contract deployment failed.
*/
class ContractDeploymentFailedError extends ErrorBase<
CONTRACT.CONTRACT_DEPLOYMENT_FAILED,
DefaultErrorData
> {}

/**
* Errors enum.
*/
enum CONTRACT {
CONTRACT_DEPLOYMENT_FAILED = 'CONTRACT_DEPLOYMENT_FAILED'
}

export { ContractDeploymentFailedError, CONTRACT };
1 change: 1 addition & 0 deletions packages/errors/src/model/core/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ export * from './abi';
export * from './address';
export * from './bloom';
export * from './certificate';
export * from './contract';
export * from './data';
export * from './hdnode';
export * from './keystore';
Expand Down
61 changes: 36 additions & 25 deletions packages/errors/src/types/errorTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,18 @@ import {
CertificateNotSignedError,
ContractInterfaceError,
DATA,
EIP1193,
EIP1193ChainDisconnected,
EIP1193Disconnected,
type EIP1193ProviderRpcErrorData,
EIP1193Unauthorized,
EIP1193UnsupportedMethod,
EIP1193UserRejectedRequest,
type ErrorBase,
FUNCTION,
HDNODE,
HTTP_CLIENT,
HTTPClientError,
POLL_ERROR,
type HTTPClientErrorData,
InvalidAbiDataToDecodeError,
InvalidAbiDataToEncodeError,
Expand All @@ -32,39 +39,36 @@ import {
InvalidKeystoreError,
InvalidKeystorePasswordError,
InvalidRLPError,
PollExecutionError,
type InvalidRLPErrorData,
type PollErrorData,
InvalidSecp256k1MessageHashError,
InvalidSecp256k1PrivateKeyError,
InvalidSecp256k1SignatureError,
InvalidSecp256k1SignatureRecoveryError,
JSONRPC,
JSONRPCDefaultError,
type JSONRPCErrorData,
JSONRPCInternalError,
JSONRPCInvalidParams,
JSONRPCInvalidRequest,
JSONRPCMethodNotFound,
JSONRPCParseError,
KEYSTORE,
NotImplementedError,
POLL_ERROR,
type PollErrorData,
PollExecutionError,
RLP,
SECP256K1,
TRANSACTION,
TransactionAlreadySignedError,
TransactionBodyError,
TransactionDelegationError,
TransactionNotSignedError,
FUNCTION,
NotImplementedError,
EIP1193,
type EIP1193ProviderRpcErrorData,
EIP1193UserRejectedRequest,
EIP1193Unauthorized,
EIP1193UnsupportedMethod,
EIP1193Disconnected,
EIP1193ChainDisconnected,
JSONRPC,
type JSONRPCErrorData,
JSONRPCParseError,
JSONRPCInvalidRequest,
JSONRPCMethodNotFound,
JSONRPCInvalidParams,
JSONRPCInternalError,
JSONRPCDefaultError
TransactionNotSignedError
} from '../model';
import {
CONTRACT,
ContractDeploymentFailedError
} from '../model/core/contract';

/**
* @note: REGISTER YOUR NEW FANCY ERRORS BELOW!
Expand Down Expand Up @@ -95,7 +99,8 @@ type ErrorCode =
| POLL_ERROR
| FUNCTION
| EIP1193
| JSONRPC;
| JSONRPC
| CONTRACT;

/**
* Conditional type to get the error data type from the error code.
Expand Down Expand Up @@ -156,7 +161,8 @@ const ERROR_CODES = {
POLL_ERROR,
FUNCTION,
EIP1193,
JSONRPC
JSONRPC,
CONTRACT
};

/**
Expand Down Expand Up @@ -271,7 +277,9 @@ type ErrorType<ErrorCodeT> =
? JSONRPCInternalError
: ErrorCodeT extends JSONRPC.DEFAULT
? JSONRPCDefaultError
: never;
: ErrorCodeT extends CONTRACT.CONTRACT_DEPLOYMENT_FAILED
? ContractDeploymentFailedError
: never;

/**
* Map to get the error class from the error code.
Expand Down Expand Up @@ -362,7 +370,10 @@ const ErrorClassMap = new Map<
[JSONRPC.METHOD_NOT_FOUND, JSONRPCMethodNotFound],
[JSONRPC.INVALID_PARAMS, JSONRPCInvalidParams],
[JSONRPC.INTERNAL_ERROR, JSONRPCInternalError],
[JSONRPC.DEFAULT, JSONRPCDefaultError]
[JSONRPC.DEFAULT, JSONRPCDefaultError],

// CONTRACT
[CONTRACT.CONTRACT_DEPLOYMENT_FAILED, ContractDeploymentFailedError]
]);

export {
Expand Down
Loading

0 comments on commit b7d60dd

Please sign in to comment.