-
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
4fc49d1
commit 76cbb07
Showing
11 changed files
with
1,258 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
../../.npmignore |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
{ | ||
"name": "@slangroom/ethereum", | ||
"version": "1.0.0", | ||
"dependencies": { | ||
"@slangroom/core": "workspace:*", | ||
"@slangroom/deps": "workspace:*", | ||
"@slangroom/shared": "workspace:*", | ||
"axios": "^1.5.1", | ||
"web3": "^4.1.2" | ||
}, | ||
"repository": "https://github.com/dyne/slangroom", | ||
"license": "AGPL-3.0-only", | ||
"type": "module", | ||
"main": "./build/cjs/src/index.js", | ||
"types": "./build/cjs/src/index.d.ts", | ||
"exports": { | ||
".": { | ||
"import": { | ||
"types": "./build/esm/src/index.d.ts", | ||
"default": "./build/esm/src/index.js" | ||
}, | ||
"require": { | ||
"types": "./build/cjs/src/index.d.ts", | ||
"default": "./build/cjs/src/index.js" | ||
} | ||
}, | ||
"./*": { | ||
"import": { | ||
"types": "./build/esm/src/*.d.ts", | ||
"default": "./build/esm/src/*.js" | ||
}, | ||
"require": { | ||
"types": "./build/cjs/src/*.d.ts", | ||
"default": "./build/cjs/src/*.js" | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
export * from '@slangroom/ethereum/tokens'; | ||
export * from '@slangroom/ethereum/lexer'; | ||
export * from '@slangroom/ethereum/parser'; | ||
export * from '@slangroom/ethereum/visitor'; | ||
export * from '@slangroom/ethereum/plugins'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
import { Lexer as L } from '@slangroom/deps/chevrotain'; | ||
import { allTokens } from '@slangroom/ethereum'; | ||
|
||
const Lexer = new L(allTokens); | ||
|
||
/** | ||
* Lexes the given line. | ||
*/ | ||
export const lex = (line: string) => Lexer.tokenize(line); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,158 @@ | ||
import { | ||
CstParser, | ||
type CstNode, | ||
type IToken, | ||
createSyntaxDiagramsCode, | ||
} from '@slangroom/deps/chevrotain'; | ||
import { | ||
allTokens, | ||
Read, | ||
The, | ||
Ethereum, | ||
Nonce, | ||
Bytes, | ||
Balance, | ||
Transaction, | ||
Id, | ||
After, | ||
Broadcast, | ||
Suggested, | ||
Gas, | ||
Price, | ||
Erc20, | ||
Decimals, | ||
Name, | ||
Symbol_, | ||
Total, | ||
Supply, | ||
Erc721, | ||
In, | ||
Owner, | ||
Asset, | ||
} from '@slangroom/ethereum'; | ||
import * as fs from 'node:fs'; | ||
|
||
export type PhraseCst = CstNode & { | ||
children: { | ||
ethereum: EthereumCst; | ||
erc20: Erc20Cst; | ||
erc721: Erc721Cst; | ||
}; | ||
}; | ||
|
||
export type EthereumCst = CstNode & { | ||
children: | ||
| { Nonce: [IToken] } | ||
| { Bytes: [IToken] } | ||
| { Balance: [IToken] } | ||
| { broadcast: CstNode } | ||
| { gasPrice: CstNode }; | ||
}; | ||
|
||
export type Erc20Cst = CstNode & { | ||
children: | ||
| { Decimals: [IToken] } | ||
| { Name: [IToken] } | ||
| { Symbol: [IToken] } | ||
| { Balance: [IToken] } | ||
| { totalSupply: CstNode }; | ||
}; | ||
|
||
export type Erc721Cst = CstNode & { | ||
children: { id: CstNode } | { Asset: [IToken] } | { Owner: [IToken] }; | ||
}; | ||
|
||
export type KindCst = CstNode & { | ||
children: { Sequential: [IToken] } | { Parallel: [IToken] } | { Same: [IToken] }; | ||
}; | ||
|
||
export type MethodCst = CstNode & { | ||
children: { Get: [IToken] } | { Post: [IToken] }; | ||
}; | ||
|
||
const Parser = new (class extends CstParser { | ||
constructor() { | ||
super(allTokens); | ||
this.performSelfAnalysis(); | ||
} | ||
|
||
phrase = this.RULE('phrase', () => { | ||
this.OR([ | ||
{ ALT: () => this.SUBRULE(this.#ethereum) }, | ||
{ ALT: () => this.SUBRULE(this.#erc20) }, | ||
{ ALT: () => this.SUBRULE(this.#erc721) }, | ||
]); | ||
}); | ||
|
||
#ethereum = this.RULE('ethereum', () => { | ||
this.CONSUME(Read); | ||
this.CONSUME(The); | ||
this.CONSUME(Ethereum); | ||
this.OR([ | ||
{ ALT: () => this.CONSUME(Nonce) }, | ||
{ ALT: () => this.CONSUME(Bytes) }, | ||
{ ALT: () => this.CONSUME(Balance) }, | ||
{ ALT: () => this.SUBRULE(this.#broadcast) }, | ||
{ ALT: () => this.SUBRULE(this.#gasPrice) }, | ||
]); | ||
}); | ||
|
||
#broadcast = this.RULE('broadcast', () => { | ||
this.CONSUME(Transaction); | ||
this.CONSUME(Id); | ||
this.CONSUME(After); | ||
this.CONSUME(Broadcast); | ||
}); | ||
|
||
#gasPrice = this.RULE('gasPrice', () => { | ||
this.CONSUME(Suggested); | ||
this.CONSUME(Gas); | ||
this.CONSUME(Price); | ||
}); | ||
|
||
#erc20 = this.RULE('erc20', () => { | ||
this.CONSUME(Erc20); | ||
this.OR([ | ||
{ ALT: () => this.CONSUME(Decimals) }, | ||
{ ALT: () => this.CONSUME(Name) }, | ||
{ ALT: () => this.CONSUME(Symbol_) }, | ||
{ ALT: () => this.CONSUME(Balance) }, | ||
{ ALT: () => this.SUBRULE(this.#totalSupply) }, | ||
]); | ||
}); | ||
|
||
#totalSupply = this.RULE('totalSupply', () => { | ||
this.CONSUME(Total); | ||
this.CONSUME(Supply); | ||
}); | ||
|
||
#erc721 = this.RULE('erc721', () => { | ||
this.CONSUME(Erc721); | ||
this.OR([ | ||
{ ALT: () => this.SUBRULE(this.#id) }, | ||
{ ALT: () => this.CONSUME(Owner) }, | ||
{ ALT: () => this.CONSUME(Asset) }, | ||
]); | ||
}); | ||
|
||
#id = this.RULE('id', () => { | ||
this.CONSUME(Id); | ||
this.CONSUME(In); | ||
this.CONSUME(Transaction); | ||
}); | ||
})(); | ||
|
||
export const CstVisitor = Parser.getBaseCstVisitorConstructor(); | ||
|
||
export const parse = (tokens: IToken[]) => { | ||
Parser.input = tokens; | ||
return { | ||
cst: Parser.phrase(), | ||
errors: Parser.errors, | ||
}; | ||
}; | ||
|
||
// TODO: remove | ||
const serializedGrammar = Parser.getSerializedGastProductions(); | ||
const htmlText = createSyntaxDiagramsCode(serializedGrammar); | ||
fs.writeFileSync('./generated_diagrams_eth.html', htmlText); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
import { PluginContext, PluginResult } from '@slangroom/core/plugin'; | ||
import { lex, parse, visit, EthereumRequestKind, type PhraseCst } from '@slangroom/ethereum'; | ||
import { Web3 } from 'web3'; | ||
|
||
/** | ||
* @internal | ||
*/ | ||
export const astify = (text: string) => { | ||
const lexed = lex(text); | ||
if (lexed.errors.length) return { errors: lexed.errors }; | ||
|
||
const parsed = parse(lexed.tokens); | ||
if (parsed.errors.length) return { errors: parsed.errors }; | ||
|
||
return { ast: visit(parsed.cst as PhraseCst) }; | ||
}; | ||
|
||
/** | ||
* @internal | ||
*/ | ||
export const execute = async ( | ||
ctx: PluginContext, | ||
kind: EthereumRequestKind | ||
): Promise<PluginResult> => { | ||
const web3 = new Web3(ctx.fetchConnect()[0]); | ||
|
||
if (kind == EthereumRequestKind.EthereumNonce) { | ||
const address = ctx.fetch('address') as string; | ||
const nonce = await web3.eth.getTransactionCount(address); | ||
return ctx.pass(nonce.toString()); | ||
} | ||
|
||
if (kind == EthereumRequestKind.EthereumGasPrice) { | ||
const gasPrice = await web3.eth.getGasPrice(); | ||
return ctx.pass(gasPrice.toString()); | ||
} | ||
|
||
if (kind == EthereumRequestKind.EthereumBalance) { | ||
// TODO: different statement for string and array | ||
const address = ctx.fetch('address'); | ||
if (Array.isArray(address)) { | ||
const balances = await Promise.all( | ||
address.map((addr) => web3.eth.getBalance(addr as string)) | ||
); | ||
return ctx.pass(balances.map((b) => b.toString())); | ||
} else { | ||
return ctx.pass((await web3.eth.getBalance(address as string)).toString()); | ||
} | ||
} | ||
// if (kind == EthereumRequestKind.EthereumBytes) { | ||
// const tag = ctx.fetch('transaction_id') as string; | ||
// const receipt = await web3.eth.getTransactionReceipt( | ||
// tag.startsWith('0x') ? tag : '0x' + tag | ||
// ); | ||
// if (!receipt) return ctx.fail("Transaction id doesn't exist"); | ||
// if (!receipt.status) return ctx.fail('Failed transaction'); | ||
// try { | ||
// const dataRead = receipt.logs[0]?.data?.slice(2); | ||
// return ctx.pass(dataRead); | ||
// } catch (e) { | ||
// return ctx.fail('Empty transaction'); | ||
// } | ||
// } | ||
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); | ||
return execute(ctx, ast); | ||
}; | ||
|
||
export const ethereumPlugins = new Set([EthereumPlugin]); |
Oops, something went wrong.