-
Notifications
You must be signed in to change notification settings - Fork 37
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Integrate the SC Intents Factory in the SmartContract Class #330
Changes from all commits
a5a141e
bffae4a
19a643e
48ee428
bf0a653
22aa9a6
74b0478
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
import { assert } from "chai"; | ||
import { CodeMetadata } from "./codeMetadata"; | ||
|
||
describe("test code metadata", function () { | ||
it("should test code metadata from bytes", () => { | ||
const bytes = new Uint8Array([1, 0]); | ||
const codeMetadata = CodeMetadata.fromBytes(bytes); | ||
|
||
assert.equal(codeMetadata.toString(), "0100"); | ||
assert.deepEqual(codeMetadata.toJSON(), { | ||
upgradeable: true, | ||
readable: false, | ||
payable: false, | ||
payableBySc: false | ||
}); | ||
}); | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,6 +6,7 @@ export class CodeMetadata { | |
private readable: boolean; | ||
private payable: boolean; | ||
private payableBySc: boolean; | ||
private static readonly codeMetadataLength = 2; | ||
|
||
/** | ||
* Creates a metadata object. By default, set the `upgradeable` attribute, and uset all others. | ||
|
@@ -22,6 +23,22 @@ export class CodeMetadata { | |
this.payableBySc = payableBySc | ||
} | ||
|
||
static fromBytes(bytes: Uint8Array): CodeMetadata { | ||
if (bytes.length !== this.codeMetadataLength) { | ||
return new CodeMetadata(); | ||
} | ||
Comment on lines
+27
to
+29
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Perhaps raising an exception would have worked as well? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Well, I've done it how it's done in vm-common. |
||
|
||
const byteZero = bytes[0]; | ||
const byteOne = bytes[1]; | ||
|
||
const upgradeable = (byteZero & ByteZero.Upgradeable) !== 0; | ||
const readable = (byteZero & ByteZero.Readable) !== 0; | ||
const payable = (byteOne & ByteOne.Payable) !== 0; | ||
const payableBySc = (byteOne & ByteOne.PayableBySc) !== 0; | ||
|
||
return new CodeMetadata(upgradeable, readable, payable, payableBySc); | ||
} | ||
|
||
/** | ||
* Adjust the metadata (the `upgradeable` attribute), when preparing the deployment transaction. | ||
*/ | ||
|
@@ -49,7 +66,7 @@ export class CodeMetadata { | |
togglePayableBySc(value: boolean) { | ||
this.payableBySc = value; | ||
} | ||
|
||
/** | ||
* Converts the metadata to the protocol-friendly representation. | ||
*/ | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -31,7 +31,7 @@ export interface ISmartContract { | |
export interface DeployArguments { | ||
code: ICode; | ||
codeMetadata?: ICodeMetadata; | ||
initArguments?: TypedValue[]; | ||
initArguments?: any[]; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Indeed. Not a breaking change. |
||
value?: ITransactionValue; | ||
gasLimit: IGasLimit; | ||
gasPrice?: IGasPrice; | ||
|
@@ -42,7 +42,7 @@ export interface DeployArguments { | |
export interface UpgradeArguments { | ||
code: ICode; | ||
codeMetadata?: ICodeMetadata; | ||
initArguments?: TypedValue[]; | ||
initArguments?: any[]; | ||
value?: ITransactionValue; | ||
gasLimit: IGasLimit; | ||
gasPrice?: IGasPrice; | ||
|
@@ -52,7 +52,7 @@ export interface UpgradeArguments { | |
|
||
export interface CallArguments { | ||
func: IContractFunction; | ||
args?: TypedValue[]; | ||
args?: any[]; | ||
value?: ITransactionValue; | ||
gasLimit: IGasLimit; | ||
receiver?: IAddress; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -9,14 +9,19 @@ import { bigIntToBuffer } from "./codec/utils"; | |
import { CodeMetadata } from "./codeMetadata"; | ||
import { ContractFunction } from "./function"; | ||
import { Interaction } from "./interaction"; | ||
import { CallArguments, DeployArguments, ISmartContract, QueryArguments, UpgradeArguments } from "./interface"; | ||
import { CallArguments, DeployArguments, ICodeMetadata, ISmartContract, QueryArguments, UpgradeArguments } from "./interface"; | ||
import { NativeSerializer } from "./nativeSerializer"; | ||
import { Query } from "./query"; | ||
import { ArwenVirtualMachine, ContractCallPayloadBuilder, ContractDeployPayloadBuilder, ContractUpgradePayloadBuilder } from "./transactionPayloadBuilders"; | ||
import { ArwenVirtualMachine, ContractCallPayloadBuilder, ContractUpgradePayloadBuilder } from "./transactionPayloadBuilders"; | ||
import { EndpointDefinition, TypedValue } from "./typesystem"; | ||
import { SmartContractTransactionIntentsFactory } from "../transactionIntentsFactories/smartContractTransactionIntentsFactory"; | ||
import { TransactionIntentsFactoryConfig } from "../transactionIntentsFactories/transactionIntentsFactoryConfig"; | ||
import { TransactionPayload } from "../transactionPayload"; | ||
const createKeccakHash = require("keccak"); | ||
|
||
interface IAbi { | ||
constructorDefinition: EndpointDefinition; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Theoretically, a breaking change. In practice, it's not (generally, speaking, the users will always pass a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We can't make There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's all right as it is. Practically (99.99%) not a breaking change for any client code. |
||
|
||
getEndpoints(): EndpointDefinition[]; | ||
getEndpoint(name: string | ContractFunction): EndpointDefinition; | ||
} | ||
|
@@ -110,27 +115,58 @@ export class SmartContract implements ISmartContract { | |
deploy({ deployer, code, codeMetadata, initArguments, value, gasLimit, gasPrice, chainID }: DeployArguments): Transaction { | ||
Compatibility.guardAddressIsSetAndNonZero(deployer, "'deployer' of SmartContract.deploy()", "pass the actual address to deploy()"); | ||
|
||
codeMetadata = codeMetadata || new CodeMetadata(); | ||
initArguments = initArguments || []; | ||
value = value || 0; | ||
const config = new TransactionIntentsFactoryConfig(chainID.valueOf()); | ||
const scIntentFactory = new SmartContractTransactionIntentsFactory({ | ||
config: config, | ||
abi: this.abi | ||
}); | ||
|
||
let payload = new ContractDeployPayloadBuilder() | ||
.setCode(code) | ||
.setCodeMetadata(codeMetadata) | ||
.setInitArgs(initArguments) | ||
.build(); | ||
const bytecode = Buffer.from(code.toString(), 'hex'); | ||
const metadataAsJson = this.getMetadataPropertiesAsObject(codeMetadata); | ||
|
||
let transaction = new Transaction({ | ||
receiver: Address.Zero(), | ||
const intent = scIntentFactory.createTransactionIntentForDeploy({ | ||
sender: deployer, | ||
bytecode: bytecode, | ||
gasLimit: gasLimit.valueOf(), | ||
args: initArguments, | ||
isUpgradeable: metadataAsJson.upgradeable, | ||
isReadable: metadataAsJson.readable, | ||
isPayable: metadataAsJson.payable, | ||
isPayableBySmartContract: metadataAsJson.payableBySc | ||
}); | ||
|
||
return new Transaction({ | ||
receiver: Address.fromBech32(intent.receiver), | ||
sender: Address.fromBech32(intent.sender), | ||
value: value, | ||
gasLimit: gasLimit, | ||
gasLimit: new BigNumber(intent.gasLimit).toNumber(), | ||
gasPrice: gasPrice, | ||
data: payload, | ||
data: new TransactionPayload(Buffer.from(intent.data!)), | ||
chainID: chainID | ||
}); | ||
} | ||
|
||
return transaction; | ||
private getMetadataPropertiesAsObject(codeMetadata?: ICodeMetadata): { | ||
upgradeable: boolean, | ||
readable: boolean, | ||
payable: boolean, | ||
payableBySc: boolean | ||
} { | ||
let metadata: CodeMetadata; | ||
if (codeMetadata) { | ||
metadata = CodeMetadata.fromBytes(Buffer.from(codeMetadata.toString(), "hex")); | ||
} | ||
else { | ||
metadata = new CodeMetadata(); | ||
} | ||
const metadataAsJson = metadata.toJSON() as { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Alternatively, we can directly make the fields of https://github.com/multiversx/mx-sdk-js-core/blob/main/src/smartcontracts/codeMetadata.ts Then use those, without an extra mapping to JSON. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not really good if someone uses the |
||
upgradeable: boolean, | ||
readable: boolean, | ||
payable: boolean, | ||
payableBySc: boolean | ||
}; | ||
|
||
return metadataAsJson; | ||
} | ||
|
||
/** | ||
|
@@ -141,27 +177,36 @@ export class SmartContract implements ISmartContract { | |
|
||
this.ensureHasAddress(); | ||
|
||
codeMetadata = codeMetadata || new CodeMetadata(); | ||
initArguments = initArguments || []; | ||
value = value || 0; | ||
const config = new TransactionIntentsFactoryConfig(chainID.valueOf()); | ||
const scIntentFactory = new SmartContractTransactionIntentsFactory({ | ||
config: config, | ||
abi: this.abi | ||
}); | ||
|
||
let payload = new ContractUpgradePayloadBuilder() | ||
.setCode(code) | ||
.setCodeMetadata(codeMetadata) | ||
.setInitArgs(initArguments) | ||
.build(); | ||
const bytecode = Uint8Array.from(Buffer.from(code.toString(), 'hex')); | ||
const metadataAsJson = this.getMetadataPropertiesAsObject(codeMetadata); | ||
|
||
let transaction = new Transaction({ | ||
const intent = scIntentFactory.createTransactionIntentForUpgrade({ | ||
sender: caller, | ||
receiver: this.getAddress(), | ||
contract: this.getAddress(), | ||
bytecode: bytecode, | ||
gasLimit: gasLimit.valueOf(), | ||
args: initArguments, | ||
isUpgradeable: metadataAsJson.upgradeable, | ||
isReadable: metadataAsJson.readable, | ||
isPayable: metadataAsJson.payable, | ||
isPayableBySmartContract: metadataAsJson.payableBySc | ||
}) | ||
|
||
return new Transaction({ | ||
sender: Address.fromBech32(intent.sender), | ||
receiver: Address.fromBech32(intent.receiver), | ||
value: value, | ||
gasLimit: gasLimit, | ||
gasLimit: new BigNumber(intent.gasLimit).toNumber(), | ||
gasPrice: gasPrice, | ||
data: payload, | ||
data: new TransactionPayload(Buffer.from(intent.data!)), | ||
chainID: chainID | ||
}); | ||
|
||
return transaction; | ||
} | ||
|
||
/** | ||
|
@@ -172,25 +217,32 @@ export class SmartContract implements ISmartContract { | |
|
||
this.ensureHasAddress(); | ||
|
||
const config = new TransactionIntentsFactoryConfig(chainID.valueOf()); | ||
const scIntentFactory = new SmartContractTransactionIntentsFactory({ | ||
config: config, | ||
abi: this.abi | ||
}); | ||
|
||
args = args || []; | ||
value = value || 0; | ||
|
||
let payload = new ContractCallPayloadBuilder() | ||
.setFunction(func) | ||
.setArgs(args) | ||
.build(); | ||
const intent = scIntentFactory.createTransactionIntentForExecute({ | ||
sender: caller, | ||
contractAddress: receiver ? receiver : this.getAddress(), | ||
functionName: func.toString(), | ||
gasLimit: gasLimit.valueOf(), | ||
args: args | ||
}) | ||
|
||
let transaction = new Transaction({ | ||
return new Transaction({ | ||
sender: caller, | ||
receiver: receiver ? receiver : this.getAddress(), | ||
receiver: Address.fromBech32(intent.receiver), | ||
value: value, | ||
gasLimit: gasLimit, | ||
gasLimit: new BigNumber(intent.gasLimit).toNumber(), | ||
gasPrice: gasPrice, | ||
data: payload, | ||
chainID: chainID, | ||
data: new TransactionPayload(Buffer.from(intent.data!)), | ||
chainID: chainID | ||
}); | ||
|
||
return transaction; | ||
} | ||
|
||
createQuery({ func, args, value, caller }: QueryArguments): Query { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should be unit tested.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
added unit test.