[WIP] Add a service to fill indexing gap for watched addresses #135
@ -7,9 +7,7 @@ contract SLVToken is ERC20 {
|
||||
uint256 private countA;
|
||||
uint256 private countB;
|
||||
|
||||
constructor() ERC20("Silver", "SLV") {
|
||||
_mint(msg.sender, 1000000000000000000000);
|
||||
}
|
||||
constructor() ERC20("Silver", "SLV") {}
|
||||
|
||||
function incrementCountA() public {
|
||||
countA = countA + 1;
|
||||
@ -20,4 +18,8 @@ contract SLVToken is ERC20 {
|
||||
}
|
||||
|
||||
receive() external payable {}
|
||||
|
||||
function destroy() public {
|
||||
selfdestruct(payable(msg.sender));
|
||||
}
|
||||
}
|
||||
|
@ -15,7 +15,8 @@
|
||||
"dependencies": {
|
||||
"@openzeppelin/contracts": "^4.0.0",
|
||||
"fastify": "^3.14.2",
|
||||
"hardhat": "^2.2.0"
|
||||
"hardhat": "^2.2.0",
|
||||
"solidity-create2-deployer": "^0.4.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@nomiclabs/hardhat-ethers": "^2.0.2",
|
||||
|
@ -1,7 +1,13 @@
|
||||
const fastify = require('fastify')({ logger: true });
|
||||
const hre = require("hardhat");
|
||||
const { getStorageSlotKey } = require('./utils');
|
||||
const {
|
||||
deployContract,
|
||||
isDeployed
|
||||
} = require("solidity-create2-deployer");
|
||||
|
||||
const { getStorageSlotKey, deployCreate2Factory } = require('./utils');
|
||||
|
||||
const CREATE2_FACTORY_ADDRESS = '0x4a27c059FD7E383854Ea7DE6Be9c390a795f6eE3'
|
||||
|
||||
// readiness check
|
||||
fastify.get('/v1/healthz', async (req, reply) => {
|
||||
@ -77,6 +83,20 @@ fastify.get('/v1/deploySLVContract', async (req, reply) => {
|
||||
}
|
||||
});
|
||||
|
||||
fastify.get('/v1/destroySLVContract', async (req, reply) => {
|
||||
const addr = req.query.addr;
|
||||
|
||||
const SLVToken = await hre.ethers.getContractFactory("SLVToken");
|
||||
const token = SLVToken.attach(addr);
|
||||
|
||||
const tx = await token.destroy();
|
||||
const receipt = await tx.wait();
|
||||
|
||||
return {
|
||||
blockNumber: receipt.blockNumber,
|
||||
}
|
||||
})
|
||||
|
||||
fastify.get('/v1/incrementCountA', async (req, reply) => {
|
||||
const addr = req.query.addr;
|
||||
|
||||
@ -116,6 +136,43 @@ fastify.get('/v1/getStorageKey', async (req, reply) => {
|
||||
}
|
||||
});
|
||||
|
||||
fastify.get('/v1/create2Contract', async (req, reply) => {
|
||||
const contract = req.query.contract;
|
||||
const salt = req.query.salt;
|
||||
|
||||
const provider = hre.ethers.provider;
|
||||
const signer = await hre.ethers.getSigner();
|
||||
const isFactoryDeployed = await isDeployed(CREATE2_FACTORY_ADDRESS, provider);
|
||||
|
||||
if (!isFactoryDeployed) {
|
||||
await deployCreate2Factory(provider, signer)
|
||||
}
|
||||
|
||||
const contractFactory = await hre.ethers.getContractFactory(contract);
|
||||
const bytecode = contractFactory.bytecode;
|
||||
const constructorTypes = [];
|
||||
const constructorArgs = [];
|
||||
|
||||
const { txHash, address, receipt } = await deployContract({
|
||||
salt,
|
||||
contractBytecode: bytecode,
|
||||
constructorTypes: constructorTypes,
|
||||
constructorArgs: constructorArgs,
|
||||
signer
|
||||
});
|
||||
|
||||
const success = await isDeployed(address, provider);
|
||||
|
||||
if (success) {
|
||||
return {
|
||||
address,
|
||||
txHash,
|
||||
blockNumber: receipt.blockNumber,
|
||||
blockHash: receipt.blockHash,
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
async function main() {
|
||||
try {
|
||||
await fastify.listen(3000, '0.0.0.0');
|
||||
|
@ -1,5 +1,8 @@
|
||||
const { artifacts } = require("hardhat")
|
||||
const { utils, BigNumber } = require("ethers")
|
||||
const { deployFactory } = require("solidity-create2-deployer");
|
||||
|
||||
const CREATE2_FACTORY_ACCOUNT = '0x2287Fa6efdEc6d8c3E0f4612ce551dEcf89A357A';
|
||||
|
||||
async function getStorageLayout(contractName) {
|
||||
const artifact = await artifacts.readArtifact(contractName);
|
||||
@ -34,4 +37,18 @@ async function getStorageSlotKey(contractName, variableName) {
|
||||
return key
|
||||
};
|
||||
|
||||
module.exports = { getStorageSlotKey }
|
||||
async function deployCreate2Factory(provider, signer) {
|
||||
// Send eth to account as required to deploy create2 factory contract.
|
||||
let tx = {
|
||||
to: CREATE2_FACTORY_ACCOUNT,
|
||||
value: utils.parseEther('0.01')
|
||||
}
|
||||
|
||||
const txResponse = await signer.sendTransaction(tx);
|
||||
await txResponse.wait()
|
||||
|
||||
// Deploy create2 factory contract.
|
||||
await deployFactory(provider)
|
||||
}
|
||||
|
||||
module.exports = { getStorageSlotKey, deployCreate2Factory }
|
||||
|
@ -109,6 +109,19 @@ func DeploySLVContract() (*ContractDeployed, error) {
|
||||
return &contract, nil
|
||||
}
|
||||
|
||||
func DestroySLVContract(addr string) (*ContractDestroyed, error) {
|
||||
res, err := http.Get(fmt.Sprintf("%s/v1/destroySLVContract?addr=%s", srvUrl, addr))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer res.Body.Close()
|
||||
|
||||
var data ContractDestroyed
|
||||
decoder := json.NewDecoder(res.Body)
|
||||
|
||||
return &data, decoder.Decode(&data)
|
||||
}
|
||||
|
||||
func IncrementCount(addr string, count string) (*CountIncremented, error) {
|
||||
res, err := http.Get(fmt.Sprintf("%s/v1/incrementCount%s?addr=%s", srvUrl, count, addr))
|
||||
if err != nil {
|
||||
@ -156,3 +169,21 @@ func ClearWatchedAddresses(gethRPCClient *rpc.Client) error {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func Create2Contract(contractName string, salt string) (*ContractDeployed, error) {
|
||||
res, err := http.Get(fmt.Sprintf("%s/v1/create2Contract?contract=%s&salt=%s", srvUrl, contractName, salt))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer res.Body.Close()
|
||||
|
||||
var contract ContractDeployed
|
||||
|
||||
decoder := json.NewDecoder(res.Body)
|
||||
err = decoder.Decode(&contract)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &contract, nil
|
||||
}
|
||||
|
@ -60,6 +60,8 @@ var _ = Describe("Watched address gap filling service integration test", func()
|
||||
updatedCountB2 = big.NewInt(1)
|
||||
|
||||
SLV2CountBIncrementedAt *integration.CountIncremented
|
||||
|
||||
contract1Salt = "contract1SLV"
|
||||
)
|
||||
|
||||
BeforeEach(func() {
|
||||
@ -89,7 +91,7 @@ var _ = Describe("Watched address gap filling service integration test", func()
|
||||
Expect(ipldErr).ToNot(HaveOccurred())
|
||||
|
||||
// Deploy two SLV contracts and update storage slots
|
||||
SLV1, contractErr = integration.DeploySLVContract()
|
||||
SLV1, contractErr = integration.Create2Contract("SLVToken", contract1Salt)
|
||||
Expect(contractErr).ToNot(HaveOccurred())
|
||||
|
||||
_, txErr = integration.IncrementCount(SLV1.Address, "A")
|
||||
@ -114,8 +116,12 @@ var _ = Describe("Watched address gap filling service integration test", func()
|
||||
})
|
||||
|
||||
defer It("test cleanup", func() {
|
||||
// Destroy create2 contract
|
||||
_, err := integration.DestroySLVContract(SLV1.Address)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
// Clear out watched addresses
|
||||
err := integration.ClearWatchedAddresses(gethRPCClient)
|
||||
err = integration.ClearWatchedAddresses(gethRPCClient)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
})
|
||||
|
||||
@ -206,24 +212,30 @@ var _ = Describe("Watched address gap filling service integration test", func()
|
||||
})
|
||||
|
||||
It("indexes missing past state on watching a contract from an earlier 'created_at'", func() {
|
||||
// Clear out watched addresses
|
||||
err := integration.ClearWatchedAddresses(gethRPCClient)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
// Remove SLV2 (created_at -> countB incremented) watched contract
|
||||
args := []sdtypes.WatchAddressArg{
|
||||
{
|
||||
Address: SLV2.Address,
|
||||
CreatedAt: uint64(SLV2CountBIncrementedAt.BlockNumber),
|
||||
},
|
||||
}
|
||||
ipldErr := ipldRPCClient.Call(nil, ipldMethod, statediff.Remove, args)
|
||||
Expect(ipldErr).ToNot(HaveOccurred())
|
||||
|
||||
// Watch SLV2 (created_at -> deployment) contract
|
||||
args := []sdtypes.WatchAddressArg{
|
||||
args = []sdtypes.WatchAddressArg{
|
||||
{
|
||||
Address: SLV2.Address,
|
||||
CreatedAt: uint64(SLV2.BlockNumber),
|
||||
},
|
||||
}
|
||||
ipldErr := ipldRPCClient.Call(nil, ipldMethod, statediff.Add, args)
|
||||
ipldErr = ipldRPCClient.Call(nil, ipldMethod, statediff.Add, args)
|
||||
Expect(ipldErr).ToNot(HaveOccurred())
|
||||
|
||||
// Sleep for service interval + few extra seconds
|
||||
time.Sleep(time.Duration(serviceInterval+2) * time.Second)
|
||||
|
||||
// WatchedAddresses = [SLV2]
|
||||
// WatchedAddresses = [GLD1, SLV1, SLV2]
|
||||
// SLV2, countA
|
||||
countA2Storage, err := ipldClient.StorageAt(ctx, common.HexToAddress(SLV2.Address), common.HexToHash(countAIndex), nil)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
@ -237,4 +249,73 @@ var _ = Describe("Watched address gap filling service integration test", func()
|
||||
Expect(countB2.String()).To(Equal(updatedCountB2.String()))
|
||||
})
|
||||
})
|
||||
|
||||
Context("destroyed contract redeployed and watched", func() {
|
||||
It("returns zero value for destroyed contract", func() {
|
||||
_, err := integration.DestroySLVContract(SLV1.Address)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
updatedCountA1 = big.NewInt(0)
|
||||
|
||||
operation := statediff.Remove
|
||||
args := []sdtypes.WatchAddressArg{
|
||||
{
|
||||
Address: SLV1.Address,
|
||||
CreatedAt: uint64(SLV1.BlockNumber),
|
||||
},
|
||||
}
|
||||
ipldErr := ipldRPCClient.Call(nil, ipldMethod, operation, args)
|
||||
Expect(ipldErr).ToNot(HaveOccurred())
|
||||
|
||||
// WatchedAddresses = [GLD1, SLV2]
|
||||
// SLV1, countA
|
||||
countA1Storage, err := ipldClient.StorageAt(ctx, common.HexToAddress(SLV1.Address), common.HexToHash(countAIndex), nil)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
countA1 := new(big.Int).SetBytes(countA1Storage)
|
||||
Expect(countA1.String()).To(Equal(updatedCountA1.String()))
|
||||
oldCountA1.Set(updatedCountA1)
|
||||
})
|
||||
|
||||
It("indexes state for redeployed contract", func() {
|
||||
// Redeploy contract
|
||||
SLV1, contractErr = integration.Create2Contract("SLVToken", contract1Salt)
|
||||
Expect(contractErr).ToNot(HaveOccurred())
|
||||
|
||||
// Add to watched address
|
||||
operation := statediff.Add
|
||||
args := []sdtypes.WatchAddressArg{
|
||||
{
|
||||
Address: SLV1.Address,
|
||||
CreatedAt: uint64(SLV1.BlockNumber),
|
||||
},
|
||||
}
|
||||
ipldErr := ipldRPCClient.Call(nil, ipldMethod, operation, args)
|
||||
Expect(ipldErr).ToNot(HaveOccurred())
|
||||
|
||||
// Sleep for service interval + few extra seconds
|
||||
time.Sleep(time.Duration(serviceInterval+2) * time.Second)
|
||||
|
||||
// WatchedAddresses = [GLD1, SLV2, SLV1]
|
||||
// SLV1, countA
|
||||
|
||||
// TODO: Fix old storage value fetched after redeploying to same address.
|
||||
// countA1Storage, err := ipldClient.StorageAt(ctx, common.HexToAddress(SLV1.Address), common.HexToHash(countAIndex), nil)
|
||||
// Expect(err).ToNot(HaveOccurred())
|
||||
// countA1 := new(big.Int).SetBytes(countA1Storage)
|
||||
// Expect(countA1.String()).To(Equal(updatedCountA1.String()))
|
||||
// oldCountA1.Set(updatedCountA1)
|
||||
|
||||
// Update storage slots
|
||||
_, txErr = integration.IncrementCount(SLV1.Address, "A")
|
||||
time.Sleep(sleepInterval)
|
||||
Expect(txErr).ToNot(HaveOccurred())
|
||||
updatedCountA1.Add(updatedCountA1, big.NewInt(1))
|
||||
|
||||
countA1AfterIncrementStorage, err := ipldClient.StorageAt(ctx, common.HexToAddress(SLV1.Address), common.HexToHash(countAIndex), nil)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
countA1AfterIncrement := new(big.Int).SetBytes(countA1AfterIncrementStorage)
|
||||
Expect(countA1AfterIncrement.String()).To(Equal(updatedCountA1.String()))
|
||||
oldCountA1.Set(updatedCountA1)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
Loading…
Reference in New Issue
Block a user