Skip to content

Commit

Permalink
feat: foundamental eth statements
Browse files Browse the repository at this point in the history
  • Loading branch information
albertolerda committed Oct 12, 2023
1 parent f0b500f commit 3709ff6
Show file tree
Hide file tree
Showing 11 changed files with 407 additions and 29 deletions.
3 changes: 1 addition & 2 deletions pkg/core/src/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import {
Identifier,
Into,
Output,
Pass,
Send,
To,
} from '@slangroom/core/tokens';
Expand Down Expand Up @@ -63,7 +62,7 @@ const Parser = new (class extends CstParser {
});

#sendpass = this.RULE('sendpass', () => {
this.OR([{ ALT: () => this.CONSUME(Send) }, { ALT: () => this.CONSUME(Pass) }]);
this.CONSUME(Send);
this.SUBRULE(this.#phrase);
this.CONSUME(Identifier);
this.CONSUME(And);
Expand Down
2 changes: 1 addition & 1 deletion pkg/core/src/tokens.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ export { Whitespace } from '@slangroom/shared';

export const Buzzword = createToken({
name: 'Buzzword',
pattern: /[a-z0-9]+/i,
pattern: /[a-z0-9_]+/i,
});

export const Identifier = createToken({
Expand Down
6 changes: 3 additions & 3 deletions pkg/core/test/slangroom.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,9 @@ Given I A and output into 'a'
Given I have a 'string' named 'a'
Then print 'a'
Then I pass a 'a' and B and output into 'b'
Then I pass a 'b' and c D
Then I pass a 'b' and C d and output into 'mimmo'
Then I send a 'a' and B and output into 'b'
Then I send a 'b' and c D
Then I send a 'b' and C d and output into 'mimmo'
`;
const slangroom = new Slangroom(p0, [p1, new Set([p2])]);
const res = await slangroom.execute(script);
Expand Down
8 changes: 4 additions & 4 deletions pkg/core/test/visitor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ test('generated ast is correct', async (t) => {
phrase: 'read the ethereum balance',
bindings: new Map<string, string>(),
},
"pass address 'addr' and send contract 'contract' and read the ethereum balance": {
"send address 'addr' and send contract 'contract' and read the ethereum balance": {
phrase: 'read the ethereum balance',
bindings: new Map<string, string>([
['address', 'addr'],
Expand All @@ -25,7 +25,7 @@ test('generated ast is correct', async (t) => {
phrase: 'read the ethereum balance',
bindings: new Map<string, string>(),
},
"connect to 'foo' and pass address 'addr' and send contract 'contract' and read the ethereum balance":
"connect to 'foo' and send address 'addr' and send contract 'contract' and read the ethereum balance":
{
connect: 'foo',
phrase: 'read the ethereum balance',
Expand All @@ -34,7 +34,7 @@ test('generated ast is correct', async (t) => {
['contract', 'contract'],
]),
},
"connect to 'foo' and pass address 'addr' and send contract 'contract' and read the ethereum balance and output into 'var'":
"connect to 'foo' and send address 'addr' and send contract 'contract' and read the ethereum balance and output into 'var'":
{
connect: 'foo',
phrase: 'read the ethereum balance',
Expand All @@ -51,7 +51,7 @@ test('generated ast is correct', async (t) => {
t.deepEqual(astHave, astWant);
}

const err = t.throws(() => astify("pass same 'x' and pass same 'y' and does not matter"), {
const err = t.throws(() => astify("send same 'x' and send same 'y' and does not matter"), {
instanceOf: ErrorKeyExists,
}) as ErrorKeyExists;
t.is(err.message, 'key already exists: same');
Expand Down
224 changes: 224 additions & 0 deletions pkg/ethereum/src/erc20_abi.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,224 @@
const abi = [
{
"constant": true,
"inputs": [],
"name": "name",
"outputs": [
{
"name": "",
"type": "string"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": false,
"inputs": [
{
"name": "_spender",
"type": "address"
},
{
"name": "_value",
"type": "uint256"
}
],
"name": "approve",
"outputs": [
{
"name": "",
"type": "bool"
}
],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
},
{
"constant": true,
"inputs": [],
"name": "totalSupply",
"outputs": [
{
"name": "",
"type": "uint256"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": false,
"inputs": [
{
"name": "_from",
"type": "address"
},
{
"name": "_to",
"type": "address"
},
{
"name": "_value",
"type": "uint256"
}
],
"name": "transferFrom",
"outputs": [
{
"name": "",
"type": "bool"
}
],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
},
{
"constant": true,
"inputs": [],
"name": "decimals",
"outputs": [
{
"name": "",
"type": "uint8"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [
{
"name": "_owner",
"type": "address"
}
],
"name": "balanceOf",
"outputs": [
{
"name": "balance",
"type": "uint256"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [],
"name": "symbol",
"outputs": [
{
"name": "",
"type": "string"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": false,
"inputs": [
{
"name": "_to",
"type": "address"
},
{
"name": "_value",
"type": "uint256"
}
],
"name": "transfer",
"outputs": [
{
"name": "",
"type": "bool"
}
],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
},
{
"constant": true,
"inputs": [
{
"name": "_owner",
"type": "address"
},
{
"name": "_spender",
"type": "address"
}
],
"name": "allowance",
"outputs": [
{
"name": "",
"type": "uint256"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"payable": true,
"stateMutability": "payable",
"type": "fallback"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"name": "owner",
"type": "address"
},
{
"indexed": true,
"name": "spender",
"type": "address"
},
{
"indexed": false,
"name": "value",
"type": "uint256"
}
],
"name": "Approval",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"name": "from",
"type": "address"
},
{
"indexed": true,
"name": "to",
"type": "address"
},
{
"indexed": false,
"name": "value",
"type": "uint256"
}
],
"name": "Transfer",
"type": "event"
}
]

export default abi;
4 changes: 2 additions & 2 deletions pkg/ethereum/src/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,8 @@ const Parser = new (class extends CstParser {
}

phrase = this.RULE('phrase', () => {
this.CONSUME(Read);
this.CONSUME(The);
this.OR([
{ ALT: () => this.SUBRULE(this.#ethereum) },
{ ALT: () => this.SUBRULE(this.#erc20) },
Expand All @@ -85,8 +87,6 @@ const Parser = new (class extends CstParser {
});

#ethereum = this.RULE('ethereum', () => {
this.CONSUME(Read);
this.CONSUME(The);
this.CONSUME(Ethereum);
this.OR([
{ ALT: () => this.CONSUME(Nonce) },
Expand Down
52 changes: 51 additions & 1 deletion pkg/ethereum/src/plugins.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,12 @@ import {JsonableArray } from '@slangroom/shared/jsonable';
import { lex, parse, visit, EthereumRequestKind, type PhraseCst } from '@slangroom/ethereum';
import { Web3 } from 'web3';

import erc20Abi from '@slangroom/ethereum/erc20_abi'
//const ERC721_ABI = require('./erc721_abi.json');
//const ERC721_METADATA_ABI = require('./erc721_metadata_abi.json');

const ERC721_TRANSFER_EVENT = "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"

/**
* @internal
*/
Expand Down Expand Up @@ -66,12 +72,56 @@ export const execute = async (
return ctx.fail('Empty transaction');
}
}
if (kind === EthereumRequestKind.EthereumBroadcast) {
const rawtx = ctx.fetch("transaction") as string;
const receipt = await web3.eth.sendSignedTransaction(rawtx.startsWith('0x') ? rawtx : '0x' + rawtx);
if (receipt.status) {
return ctx.pass(receipt.transactionHash.toString().substring(2)); // Remove 0x
} else {
throw new Error("Transaction failed");
}
}
const erc20_0 = new Map([
[EthereumRequestKind.Erc20Symbol, "symbol()"],
[EthereumRequestKind.Erc20Decimals, "decimals()"],
[EthereumRequestKind.Erc20Name, "name()"],
[EthereumRequestKind.Erc20TotalSupply, "totalSupply()"],
])
if (erc20_0.has(kind)) {
const sc = ctx.fetch("sc") as string;
if(!web3.utils.isAddress(sc)) {
throw new Error(`Not an ethereum address ${sc}`);
}
const erc20 = new web3.eth.Contract(erc20Abi, sc);
return ctx.pass(await erc20.methods[erc20_0.get(kind) || ""]?.().call() || "");
}
/*if (kind === EthereumRequestKind.Erc20Balance) {
const sc = ctx.fetch("sc") as string;
//const address = ctx.fetch("address") as string;
if(!web3.utils.isAddress(sc)) {
throw new Error(`Not an ethereum address ${sc}`);
}
const erc20 = new web3.eth.Contract(erc20Abi, sc);
console.log(erc20.methods['balanceOf']?.('ciccio'))
return ctx.pass(await erc20.methods?.["balanceOf"]?.()?.call() || "");
}*/
if (kind === EthereumRequestKind.Erc721Id) {
const tag = ctx.fetch('transaction_id') as string;
const receipt = await web3.eth.getTransactionReceipt(tag.startsWith('0x') ? tag : '0x' + tag);
const log = receipt.logs.find(
v => v && v.topics && v.topics.length > 0 && v.topics[0] === ERC721_TRANSFER_EVENT);
if(!log || !log.topics) {
throw new Error("Token Id not found")
}
return ctx.pass(parseInt(log.topics[3]?.toString() || "", 16));
}
return ctx.fail('Should not be here');
};

const EthereumPlugin = async (ctx: PluginContext): Promise<PluginResult> => {
const { ast, errors } = astify(ctx.phrase);
if (!ast) return ctx.fail(errors);
if (errors) return ctx.fail(errors);
return execute(ctx, ast);
};

Expand Down
Loading

0 comments on commit 3709ff6

Please sign in to comment.