buidler-labs / hashgraph-venin-js Goto Github PK
View Code? Open in Web Editor NEWThe, service over-arching, Hedera Stratospheric SDK for JS developers
Home Page: https://venin.buidlerlabs.com
License: MIT License
The, service over-arching, Hedera Stratospheric SDK for JS developers
Home Page: https://venin.buidlerlabs.com
License: MIT License
Following #48 , we added increased cost-control for LiveContract
dynamically binded ABI entities (eg. methods).
From an UX standpoint, there is no point in returning only the receipt for non-mutating (ContractCallQuery
s) calls. The dev should be all the time interested in the query result in this case since the state of the contract will never change under these circumstances.
Are there other dev flows that we could improve for only-receipts execution?
... similar to how we implemented StratoAddress.toLiveContract()
The plugin should generate and make available a ContractRegistry
object for browser dApp consumption.
ContractRegistry
will be a JS object having the keys taking the name of the contracts to use with their coresponding value its ABI in a Human-Readable format.
Eg. for a SimpleStorage
contract, you would have ContractRegistry
be:
{
"SimpleStorage" : [
"function get() view returns (uint256)",
"function num() view returns (uint256)",
"function set(uint256 _num)"
]
}
This will then allow to instantiate, in browser, LiveContracts
without the need to compile the source code.
Eg:
await session.getLiveContract({ id, abi: ContractRegistry.SimpleStorage })
The plugin should be configurable via an options object containing, for now, a includeCompiler
boolean (default false
) which, when set to true, will allow for contract-compilation (via methods such as Contract.newFrom({ code })
) in browser.
Note:
When updating the docs, make sure to specify that including the compiler is a bandwidth intensive operation. The browser will have to download more then 20+MB.
As part of our dependency tree, we have the babel-plugin-istanbul
package which conflicts with Jest's coveragePathIgnorePatterns
as their documentation states.
Basically, this prop gets ignored and everything gets included in the coverage report like so:
We need to find a way to bypass this and have proper coverage reporting provided.
When embedding the docs link ( https://hsj-docs.buidlerlabs.com/ ) to a messenger app such as skype
, the resulting render contains code that should not be present there and replaced with something more SEO friendly.
To fix this, we would end up moving the both the OperatorId
and the OperatorNetwork
react components and refactor them somewhere else. This needs addressing in any way since that part is duplicated between introduction.md
and playground.md
.
We need to allow for browser smoke tests to be carried out.
For this to happen, we need a tweaked self-runner instance that can run a headless chrome.
Having a contract such as:
contract Bar {
function foo(address addr) external pure returns (address) {
return addr;
}
}
should allow pseudo-code callings such as liveContract.foo(ContractId | AccountId | TokenId | ... )
.
Which would end up using the <ContractId | AccountId | TokenId | ... >.toSolidityAddress()
as the address addr
argument.
Basically, we should look at all the types of Hedera Id
s and auto-map, through this principle, every type that has a .toSolidityAddress()
function.
We need a way to retrieve a reference to a deployed token given its Id, similar to what we do with LiveContract
s and LiveJson
s
hedera-services
's v0.26
has brought the capacity to throw require
messages out of smart contract executions. Here is a transaction that has such an error thrown inside its error_message
property.
We need to support this!
Ether's interface.decodeErrorResult
should be used to parse the error message.
We need to discuss further mechanics:
try
/catch
mechanism sufficient to get this error across to dev-land?LiveFile
(inherited by LiveJson
) is exposing a getContents()
method to retrieve the underlying content as a Promise<Uint8Array>
.
We need to have:
getContents
called update
which is a boolean defaulting to false
that, when set to true
does a FileContentsQuery
network action, updating the underlying content in the process. If it's false, it retrieves the stored data in Uint8Array
format.LiveJson
and LiveFile
should use the query result to update it's internal data
referenceLiveJson.toString()
which will stringify its dataLiveFile.toString()
which will return its internal data (if it's a string) or <binary File content>
if the data is a Uint8Array
.toString()
implementations will allow for fluent formatting in structures such as This is the file: ${liveFile}
.
Currently, ApiSession.execute
supports 3 return types:
Regardless of the return type selected, if Executable
is a contract transaction (ContractCallQuery
or ContractExecuteTransaction
) a costly TransactionRecordQuery
is emitted.
We will add a 4th type, TypeOfExecutionReturn.OnlyReceipt
, which will only return receipts without doing other operations that cost crypto.
We will add control logic to LiveContract
calls to via a new meta-argument called onlyReceipt
(boolean) to only return receipts without doing other operations that cost crypto if desired. This shall also be accomplished by a new session default called onlyReceiptsFromContractRequests
with its associated environment value of HEDERAS_DEFAULT_CONTRACT_REQUESTS_RETURN_ONLY_RECEIPTS
(boolean, default true
).
We need a way to lock onto an existing LiveTopic
so that we can interact with it cross-ApiSession
. This shall be done in a similar manner to how we currently have ApiSession.getLiveContract
or ApiSession.getLiveJson
.
Current ApiSession
execute
method is using a getReceipt
boolean flag which we would like to remove.
We think this flag is extra considering the returnType
which can be: TypeOfExecutionReturn.Record
, TypeOfExecutionReturn.Receipt
or TypeOfExecutionReturn.Result
.
If we do get a receipt, considering the returnType
, we want to emit it if there are any listeners subscribed for TRANSACTION_ON_RECEIPT_EVENT_NAME
event.
Considering the fact that there are multiple types of transactions which are done, we want to give user the ability to filter out some of this transactions if needed.
It would be great to have use a wrapper for the EventEmitter
so we can filter some of these transactions out, based on transaction type and maybe also by the status
on the receipt.
Permit loading of abstract contracts even though, by default, this won't result in any byteCode
being generated by the Solidity compiler.
So, for example, the following solidity contract:
abstract contract SimpleStorage {
uint public num;
function set(uint _num) public {
num = _num;
}
function get() public view returns (uint) {
return num;
}
}
even though all of its methods have an implementation, will result in its solo.evm.bytecode.object
being ''. Even though it will correctly extract the ABI.
We are allowing this mostly so that the ContractRegistry
is able to get generated correctly when bundling (it only needs the ABI in this scenario).
When this is the case, be sure not to allow uploading of the Contract
through a ApiSession
in an attempt to make it live.
This has been supported since version 2.9.0 of the sdk and we should also support this optimization.
Once a developer calls LiveEntity.deleteEntity()
, all network operations following it should error out since, if deletion is successful, the entity won't be live anymore.
For reference, deleting LiveEntity
s was added in #19
To avoid name clashes due to dynamically binding the ABI methods on the LiveContract
instance, we should expose/elevate the LiveEntity
operations on a different level. The naming of it remains to be discussed, but it will have to be something like LiveContract.self.deleteEntity()
(note the self
part).
This will allow to rename the LiveEntity
operations into something lighter such as LiveContract.self.delete()
.
It looks like if you're uploading a contract with insufficient gas, you get a wall-of-text
printed on CLI:
StatusError: receipt for transaction [email protected] contained error status INSUFFICIENT_GAS
/home/vic/projects/3vs/headstarter-contracts/node_modules/solc/soljson.js:1
null;var Module=typeof Module!=="undefined"?Module:{};var moduleOverrides={};var key;for(key in Module){if(Module.hasOwnProperty(key)){moduleOverrides[key]=Module[key]}}var arguments_=[];var thisProgram="./this.program";var quit_=function(status,toThrow){throw toThrow};var ENVIRONMENT_IS_WEB=false;var ENVIRONMENT_
...
RuntimeError: abort(StatusError: receipt for transaction [email protected] contained error status INSUFFICIENT_GAS). Build with -s ASSERTIONS=1 for more info.
at process.abort (/home/vic/projects/3vs/headstarter-contracts/node_modules/solc/soljson.js:1:13012)
at process.emit (node:events:527:28)
at emit (node:internal/process/promises:140:20)
at processPromiseRejections (node:internal/process/promises:274:27)
at processTicksAndRejections (node:internal/process/task_queues:97:32)
Node.js v17.8.0
It very much looks like the huge chunk of text is the solidity compiler itself.
We need to not let this happen and protect the developer's CLI from 25MB of non-sensical gibberish.
The problem might also be in the hedera SDK itself. We don't know that for sure.
We already support LiveAddress.equals(string)
and, of course, LiveAddress.equals(LiveAddress)
.
Might as well allow for AccountId
comparisons.
This is already being used in our test-base and could simplify coding.
Make sure tests pass
The SDK does not support calling smart-contract methods with complex data-structures such as the storeGroup
method from the following listing:
// Source: https://discordapp.com/channels/373889138199494658/909532351388864542/967985376037859329 (community post on hedera#smart-contracts)
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.3;
contract GroupStore {
struct Group {
uint8 groupId;
string groupName;
Member[] members;
}
struct Member {
uint8 memberId;
string memberName;
}
function storeGroup(Group[] memory _groups) public pure returns (Group[] memory) {
return _groups;
}
}
To make this possible, we would have to encode the calling of the function along with its parameters through ether's Interface.encodeFunctionData method and attach the hex-encoded result to the request
via setFunctionParameters
. Something like this in LiveContract.createContractRequestFor
:
const encodedFuncParams = this.interface
.encodeFunctionData(fDescription.name, args)
.slice(2);
const funcParamsBuffer = Buffer.from(encodedFuncParams, "hex");
request.setFunctionParameters(funcParamsBuffer);
which will need to replace/augment:
request.setFunction(
fDescription.name,
await ContractFunctionParameters.newFor(fDescription, args)
);
Note: We already use ethers
on the receiving end to decode/parse the function result (via Interface.decodeFunctionResult
)
P.S.: Add the following test and make sure it is working after this gets implemented:
it("calling a live-contract function with complex nested object parameters should be permitted", async () => {
const { liveContract } = await load("group_store");
const groups = [
{
groupId: 1,
groupName: "lighters",
members: [
{
memberId: 2,
memberName: "Luke",
},
{
memberId: 3,
memberName: "Obi One",
},
],
},
{
groupId: 2,
groupName: "darkers",
members: [
{
memberId: 5,
memberName: "Vader",
},
],
},
];
await expect(liveContract.storeGroup(groups)).resolves.toEqual(groups);
});
Don't forget to cleanup ContractFunctionParameters
once done.
Currently, we issue a TransactionRecordQuery
request if we want smart-contract execution logs following a ContractExecuteTransaction
call. For ContractCallQuery
there is no such requirement since, by design, the ContractCallQuery
response (which is a ContractFunctionResult
) contains those logs.
Such a transaction is costly but there is a way around this: we poll for the logs from the mirror-node via GET-ing on /api/v1/contract/{contractId}/results/logs
.
Now this is admittedly a burdensome strategy since it will put load on the mirror-node, yet since other libraries are doing it (eg. hethers
), we might as well go down this path for cost-efficiency. Maybe we have this polling enabled via a flag? That's worth thinking about.
Currently ( following #32 ), generating a ContractRegistry
from the following contracts
:
a.sol
inner/b.sol
with a.sol
containing contract A
and b.sol
containing contract B; contract C
yeilds
{
"a": <contract A ABI>,
"inner/b": <contract B ABI>
}
and contract C
gets discarded because we only support one contract reference per ContractRegistry
entry.
Ideally, we would like for ContractRegistry
to reference all file-parsed contracts:
{
"a#A": <contract A ABI>,
"inner/b#B": <contract B ABI>,
"inner/b#C": <contract C ABI>,
}
Under these circumstances, are we ok with the property naming?
ApiSession.execute
should allow transactions to be signed by multiple Signer
s before being dispatched to the network.
This only will need to be carried out for Transaction
s through their Transaction.signWithSigner(signer)
method since Query
s don't require or support this multi-signature support.
This also means that we will only support HIP-338
compatible Signer
s for this feature.
We need to provide the ApiSession
instance with a default list of Signer
s so that all Transaction
s dispatched through that instance's ApiSession.execute
are implicitly signed
through those Signer
s before dispatching.
In case of LiveContract
s we also need to provide meta-arguments to allow signer
overrides or ad-hoc usage that bypass the ApiSession
default lists.
Let's discuss the specifics of this feature on this thread.
Normally, Strato should allow for HEDERAS_DEFAULT_PAYMENT_FOR_CONTRACT_QUERY
to be optional in which case, have the SDK compute an acceptable cost for the transaction taking into account the upper maxPaymentCost
set on the client.
This doesn't look like it's working and we need to understand if this is something that we are doing wrong or if it's something broken in the sdk.
Currently, test/utils.ts
is exporting individual functions such as:
most of which have a relativeTo
path argument that we the wrap in all different test-groups such as:
This adds unnecessary LOCs to the test definition files which can be avoided if we would work with a utility-object instance instead which would let us instantiate it per testing-module similar to: new TestingUtility(relativeTo: string)
which we would then use normally via calls such as testingUtility.read(...)
.
Similar to ApiSession.getLiveJson
or ApiSession.getLiveContract
, this is needed to retrieve a generically hosted, LiveFile
, content (string or binary) from the network.
2048
bytes4096
_transactionValidDuration
of 180
seconds (via Transaction.setTransactionValidDuration
method) which is the maxed-out value present in the docsTest this contract for array returned values.
This comes from a Discord user and it would be nice to have.
Given the following solidity file:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;
contract Square {
uint public size;
constructor(uint _size) payable {
size = _size;
}
}
contract SquareCreator {
function create(uint _size) public returns (Square Square_) {
return new Square(_size);
}
}
it would be nice if we would support doing this:
const liveSquare = await squareCreatorLiveContract.create(10);
console.log(await liveSquare.size());
This will allow chaining contract type calls which will further stream-line the development experience.
To make this working, we need a way to understand that a method return type is an actual contract. @ethersproject/abi
's Interface
does not provide too much help in this regards:
even though the solidity compiler does appear to export more info regarding the raw internalType
of the output:
One way to avoid this is to try and have a naming convention in place similar to the one present in the above example where we could say that 'if a returned variable name ends with an underscore, then try to map its address to a live-contract and return that'. If we go on this path, we need to settle on this convention. For flexibility reasons, It would be nice if we could avoid enforcing this convention and take advantage of solidity's internalType
.
Another issue that needs to be addressed for this feature to be implemented is the parser's access to the ABIs for which the resulting live-contracts are built. Basically, in our previous example case, this is the equivalent to answering the question of how to make available the Square
ABI to the create
result mapper. What happens if the returned contract type is not part of the same solidity file (eg. import path)?
This feature should be flagged by a context config-parameter.
We have the following solidity contract as part of our solidity-by-example
test group:
library Array {
function remove(uint[] storage arr, uint index) public {
// Move the last element into the place to delete
require(arr.length > 0, "Can't remove from empty array");
arr[index] = arr[arr.length - 1];
arr.pop();
}
}
contract TestArray {
using Array for uint[];
uint[] public arr;
function testArrayRemove() public {
for (uint i = 0; i < 3; i++) {
arr.push(i);
}
arr.remove(1);
assert(arr.length == 2);
assert(arr[0] == 0);
assert(arr[1] == 2);
}
}
When compiling the TestArray
contract, Solidity does not expand the dependent Array
library in the resulting byte-code:
which fails Contract
instantiation.
Currently, we are using runtime export matchers (eg. node
) to differentiate between esm
and cjs
servings. The @hashgraph/sdk
uses syntactic export matchers (eg 'import/
require`).
We need to have Strato use the same package.json export strategy as Hedera if we want to avoid potential hard-to-debug issues such as mismatch class finger-printing (failing of instanceof
calls within the sdk, for instance).
This will make the ContractRegistry
a session-dependent entity even though it won't be a LiveEntity
.
And while we are here, move ApiSession.getLiveContract
to the associated ApiSession.ContractRegistry
instance.
This will allow more fluent calls such as session.contractRegistry[<name_of_loaded_contract>].getLiveAt({ id })
.
Special considerations should be thought for web-runtime contexts. Specifically, the strato-rollup plugin will need to be updated.
We should do this after #33 but lets start discussing it: How will the resulting API be like? Any concerns?
... that way, the user will know that stuff is happening and that execution is ongoing.
We should remove the loading animation when the execution finishes.
Hedera Consensus Service (HCS) is made out of topics which means that this will most likely be a CreatableEntity
variant to allow for session.create(topic)
operations.
Once we release the Strato Rollup Plugin we are ready to extract the docs
(and their lib.docs
sources + package.json
dependencies) and host them on a separate branch. That way, we'll lighten up the main
branch a bit (or maybe a lot?).
import { Contract } from '@buidlerlabs/hedera-strato-js';
await Contract.newFrom({ path: './hello.sol' });
should work in a pure node-js ESM runtime environment but it does not. It throws a
TypeError: solc.compile is not a function
at Function.compile (.../node_modules/@buidlerlabs/hedera-strato-js/lib.esm/SolidityCompiler.mjs:78:17)
error.
Other runtime-environments/module resolution mechanics (eg. node-js/require and web/imports) should work.
Reported on 0.7.5
It looks like bignumber.js
is getting bundled into strato. That's not ok. We need to have it externally provided via a type module script.
This is so that BigNumber functionality can work with LiveContract
arguments/return values.
Currently, if we are calling a live contract function that has any type of bytes
argument, we do not apply arrayify
which is available in @ethersproject/bytes
which we already have in the project as part of the @ethersproject/abi
package.
For example, if we have a hex string (eg. merkle tree root) which we we want to send to a function containing a bytes32
argument, the function call will fail as hedera-sdk-js
will expect to always receive a Uint8Array
with a length of 32.
Currently, conditional export rules implemented by #22 and present in package.json expose ContractRegistry
for all ES module runtimes.
This makes type: module
node runtimes break.
We need to provide a solid ContractRegistry
so that node environments can make use of while bundlers might dynamically generate contract-registry code.
If we're returning a single bytes
variant from a solidity smart contract (eg. bytes32
), Strato returns back to the caller the hex-string encoding of the result (eg. '0x23a0...'
). We need it to return the actual array of bytes (Uint8Array
).
Example of such function (taken from solidity-by-example>signature code):
function getEthSignedMessageHash(bytes32 _messageHash)
public
pure
returns (bytes32)
{
return
keccak256(
abi.encodePacked("\x19Ethereum Signed Message:\n32", _messageHash)
);
}
HIP-338 deals with wallet integrations and providers. Depending on how fast will the things happen on the SDK and the stability of it, this will be an upcoming priority for Strato.
Details will be discussed on this ticket.
I think this will be a beast to tackle, but it would be amazing if we could do it: make strato work in a react-native runtime environment.
Be sure to thoroughly document this journey.
Our LiveTopic
can submitMessage
s. How about giving it powers to consume/subscribe to topics?
Hedera docs: https://docs.hedera.com/guides/docs/sdks/consensus/get-topic-message
It's a common creatable/uploadable live entity behavior. Might as well support it.
A developer can delete:
Following #22 , we will be adding code to support bundling with rollup. This will put some more dependencies (mostly rollup core + plugins) into strato's package.json dependencies
list.
These are:
That's acceptable for now, but if we plan to support multiple bundlers (eg. webpack), we need to pull those out and have them distributed as a separate package (eg. @buidlerlabs/hedera-strato-rollup
).
Be careful to move these back in devDependencies
when this will wrap up to allow rollup bundlers (such as for docs and the ones used for testing) still work.
So that StratoAddress.toLiveContract
can move away from ContractId.fromSolidityAddress
and use the new ContractId.fromEvmAddress
.
When bundling Strato with Rollup, there is an option to recurse (contracts.recurse
) which, when set to true
, it should recursively load all the solidity files from all directory levels found at contracts.path
.
It does not work: The generated ContractRegistry
maintains a flatten <contract name>: <contract ABI>
reference from all processed solidity files. This means that if there's a contract A
located in a.sol
and a contract A
located at b/a.sol
, there will be 2 entries with the same key in ContractRegistry
:
{
A: <a.sol A contract ABI>,
A: <b/a.sol A contract ABI>
}
which is an invalid state.
The correct prop names should come close to the ones present in #33 :
{
A: <a.sol A contract ABI>,
b/A: <b/a.sol A contract ABI>
}
Note:
The props currently don't contain the name of the solidity file itself (as present in #33 's description) . This means that if, for the above example, there were also a contract A
present in b/c.sol
, you would again hit the same name conflict issue. We will resolve this on a separate ticket.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.