Skip to content
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

Remove Ethers Dependencies #117

Merged
merged 8 commits into from
May 8, 2022
28 changes: 10 additions & 18 deletions dmap.js
Original file line number Diff line number Diff line change
@@ -1,19 +1,15 @@
const ebnf = require('ebnf')
const ethers = require('ethers')

const pack = require('./pack/dmap.json')
const artifact = require('./pack/ipfs/Dmap.json')

const abi = artifact.abi
const dmap_i = new ethers.utils.Interface(abi)
const dmap_utils = require("./utils/dmap_utils")

const dmap_address = pack.objects.dmap.address

const fail =s=> { throw new Error(s) }
const need =(b,s)=> b || fail(s)

const coder = ethers.utils.defaultAbiCoder
const keccak256 = ethers.utils.keccak256

module.exports = lib = {}

lib.address = dmap_address
Expand Down Expand Up @@ -41,8 +37,8 @@ lib.parse =s=> {
}

lib.get = async (dmap, slot) => {
const nextslot = ethers.utils.hexZeroPad(
ethers.BigNumber.from(slot).add(1).toHexString(), 32
const nextslot = dmap_utils.hexZeroPad(
dmap_utils.hexlify(BigInt(slot) + BigInt(1)), 32
)
let meta, data
await Promise.all(
Expand All @@ -51,28 +47,24 @@ lib.get = async (dmap, slot) => {
dmap.provider.getStorageAt(dmap.address, nextslot)
]
).then(res => [meta, data] = res)
const resdata = dmap_i.encodeFunctionResult("get", [meta, data])
const res = dmap_i.decodeFunctionResult("get", resdata)
return res
return [meta, data]
}

lib.getByZoneAndName = async (dmap, zone, name) => {
const slot = keccak256(coder.encode(["address", "bytes32"], [zone, name]))
const slot = dmap_utils.keccak256(dmap_utils.encodeZoneAndName(zone, name));
return lib.get(dmap, slot)
}

lib.set = async (dmap, name, meta, data) => {
const calldata = dmap_i.encodeFunctionData("set", [name, meta, data])
const calldata = dmap_utils.encodeFunctionCallBytes32Args("set(bytes32,bytes32,bytes32)", [name, meta, data])
return dmap.signer.sendTransaction({to: dmap.address, data: calldata})
}

const slotabi = ["function slot(bytes32 s) external view returns (bytes32)"]
const slot_i = new ethers.utils.Interface(slotabi)
// const slotabi = ["function slot(bytes32 s) external view returns (bytes32)"]
// const slot_i = new ethers.utils.Interface(slotabi)
lib.slot = async (dmap, slot) => {
const val = await dmap.provider.getStorageAt(dmap.address, slot)
const resdata = slot_i.encodeFunctionResult("slot", [val])
const res = slot_i.decodeFunctionResult("slot", resdata)
return res[0]
return val
}


Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
},
"dependencies": {
"ebnf": "^1.9.0",
"js-sha3": "^0.8.0",
"tapzero": "^0.6.1"
}
}
8 changes: 4 additions & 4 deletions test/utils/helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -67,16 +67,16 @@ const check_entry = async (dmap, usr, key, _meta, _data) => {
const meta = typeof(_meta) == 'string' ? _meta : '0x'+_meta.toString('hex')
const data = typeof(_data) == 'string' ? _data : '0x'+_data.toString('hex')
const resZoneName = await lib.getByZoneAndName(dmap, usr, key)
want(resZoneName.meta).to.eql(meta)
want(resZoneName.data).to.eql(data)
want(resZoneName[0]).to.eql(meta)
want(resZoneName[1]).to.eql(data)
want(resZoneName).to.eql([meta, data])

const coder = ethers.utils.defaultAbiCoder
const keccak256 = ethers.utils.keccak256
const slot = keccak256(coder.encode(["address", "bytes32"], [usr, key]))
const resGet = await testlib.get(dmap, slot)
want(resGet.meta).to.eql(meta)
want(resGet.data).to.eql(data)
want(resGet[0]).to.eql(meta)
want(resGet[1]).to.eql(data)
want(resGet).to.eql([meta, data])

const nextslot = ethers.utils.hexZeroPad(
Expand Down
120 changes: 120 additions & 0 deletions utils/dmap_utils.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
const kek = require('js-sha3')

module.exports = {
hexZeroPad,
hexlify,
keccak256,
encodeZoneAndName,
encodeFunctionCallBytes32Args
}

// GLOBAL TODO: !DMFXYZ! error and bounds checking for inputs
const HexCharacters = "0123456789abcdef";

function hexZeroPad(value, length) {
if (typeof(value) !== "string") {
value = hexlify(value);
}

if (value.length > 2 * length + 2) {
throw "Value too big"
}

while (value.length < 2 * length + 2) {
value = "0x0" + value.substring(2);
}

return value;
}

function hexlify(value) {

if (typeof(value) === "number") {
let hex = "";
while (value) {
hex = HexCharacters[value & 0xf] + hex;
value = Math.floor(value / 16); // can bitshift instead
}

if (hex.length) {
if (hex.length % 2) {
hex = "0" + hex;
}
return "0x" + hex;
}

return "0x00";
}

if (typeof(value) === "bigint") {
value = value.toString(16);
if (value.length % 2) {
return ("0x0" + value);
}
return "0x" + value;
}

if (typeof(value) === 'string') {
return Buffer.from(value).toString('hex');
}
}

// Assumes value is a hex encoded string for now, or already a byte array
function keccak256(value) {

if (typeof(value) == "string") {
return "0x" + kek.keccak256(new Uint8Array(_toBytes(value)));
}
// add back in prefix and return as unsigned 1byte int array
return "0x" + kek.keccak256(value);
}

function encodeZoneAndName(zone, name) {
// zone should be an address, start by zero-padding 12 bytes
let params = '0x' + '00'.repeat(12);
if (zone.length == 0) {
params = params + '00'.repeat(20);
} else {
params = params + zone.slice(2); // assume has leading 0x, prob shouldn't do this
}
if (name.length == 0 || name == null) {
params = params + '00'.repeat(32);
} else if (typeof(name) == 'object') {
// if an object, create a buffer from data and encode as hex string
params = params + Buffer.from(name).toString('hex');
} else {
// if alredy a hex string, just drop the 0x
params = params + name.slice(2);
}
return params;
}

function encodeFunctionCallBytes32Args(signature, args) {
const signature_as_buffer = Buffer.from(signature)
// calculate function selector as first 4 bytes of hashed signature
// keccak256 returns a string, so we take the first 10 characters
const selector = keccak256(signature_as_buffer).slice(0, 10)
let calldata = selector
for (i = 0; i < args.length; ++i) {
calldata += Buffer.from(_toBytes(args[i])).toString('hex');
}
return calldata;

}

function _toBytes(value) {
if (typeof(value) == 'string') {
if (value.substring(0, 2) == "0x") {
value = value.substring(2)
}
// Need to create an array of bytes from hex string
// just grab 2 4-byte hex symbols at a time and parse them as base16
const bytes_array = []
for (let i = 0; i < value.length; i += 2) {
bytes_array.push(parseInt(value.substring(i, i + 2), 16));
}
return bytes_array
}
// otherwise just return the object
return value
}