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

Improve local test utils #109

Merged
merged 5 commits into from
Nov 25, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 8 additions & 4 deletions local-test-configuration/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ the setup:
different API. One of the APIs is delayed so that the beacons have different values.
- Use 2 signed APIs and each Pusher pushes to a separate Signed API.
- We run Airseeker only on Hardhat network for setup simplicity. Initially, I wanted to have a Polygon testnet as well,
but gave up on that idea for now.
but gave up on that idea for now. The setup can be easily extended to run on any chain, just by not starting Hardhat
network and using a different RPCs.

All configurations are based on the example files, but have been slightly modified. I had to also choose more volatile
assets from the Nodary API to see Airseeker updates.
Expand All @@ -25,13 +26,13 @@ assets from the Nodary API to see Airseeker updates.
- Start Signed API 1 on port `4001` (in a separate terminal):

```sh
docker run --publish 4001:8090 -it --init --volume $(pwd)/local-test-configuration/signed-api-1:/app/config --env-file ./local-test-configuration/signed-api-1/.env --rm --memory=256m api3/signed-api:latest
docker run --publish 4001:80 -it --init --volume $(pwd)/local-test-configuration/signed-api-1:/app/config --env-file ./local-test-configuration/signed-api-1/.env --rm --memory=256m api3/signed-api:latest
```

- Start Signed API 2 on port `4002` (in a separate terminal):

```sh
docker run --publish 4002:8090 -it --init --volume $(pwd)/local-test-configuration/signed-api-2:/app/config --env-file ./local-test-configuration/signed-api-2/.env --rm --memory=256m api3/signed-api:latest
docker run --publish 4002:80 -it --init --volume $(pwd)/local-test-configuration/signed-api-2:/app/config --env-file ./local-test-configuration/signed-api-2/.env --rm --memory=256m api3/signed-api:latest
```

You can go to `http://localhost:4001/` and `http://localhost:4002/` to see the Signed API 1 and 2 respectively.
Expand Down Expand Up @@ -65,9 +66,12 @@ also required for the monitoring page.

- Open the monitoring page located in `local-test-configuration/monitoring/index.html` in a browser with the following
query parameters appended
`api3ServerV1Address=<DEPLOYED_API3_SERVER_V1_ADDRESS>&dapiDataRegistryAddress=<DEPLOYED_DAPI_DATA_REGISTRY_ADDRESS>`
`?api3ServerV1Address=<DEPLOYED_API3_SERVER_V1_ADDRESS>&dapiDataRegistryAddress=<DEPLOYED_DAPI_DATA_REGISTRY_ADDRESS>&rpcUrl=<RPC_URL>&airseekerMnemonic=<AIRSEEKER_MNEMONIC>`
and open console.

The `AIRSEEKER_MNEMONIC` needs to be URI encoded via `encodeURIComponent` in JS. For example,
`test%20test%20test%20test%20test%20test%20test%20test%20test%20test%20test%20junk`.

Initially, you should see errors because the beacons are not initialized. After you run Airseeker, it will do the
updates and the errors should be gone. The page constantly polls the chain and respective signed APIs and compares the
on-chain and off-chain values. If the deviation exceeds the treshold, the value is marked bold and should be updated by
Expand Down
33 changes: 29 additions & 4 deletions local-test-configuration/monitoring/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -1449,9 +1449,10 @@ <h2>Active dAPIs</h2>

// Configuration
const urlParams = new URLSearchParams(window.location.search);
const rpcUrl = 'http://127.0.0.1:8545',
const rpcUrl = urlParams.get('rpcUrl'),
api3ServerV1Address = urlParams.get('api3ServerV1Address'),
dapiDataRegistryAddress = urlParams.get('dapiDataRegistryAddress');
dapiDataRegistryAddress = urlParams.get('dapiDataRegistryAddress'),
airseekerMnemonic = decodeURIComponent(urlParams.get('airseekerMnemonic'));

if (!api3ServerV1Address) throw new Error('api3ServerV1Address must be provided as URL parameter');
if (!dapiDataRegistryAddress) throw new Error('dapiDataRegistryAddress must be provided as URL parameter');
Expand Down Expand Up @@ -1534,6 +1535,28 @@ <h2>Active dAPIs</h2>
return updateInPercentage.gt(deviationThreshold);
};

function deriveWalletPathFromSponsorAddress(sponsorAddress) {
const sponsorAddressBN = ethers.BigNumber.from(sponsorAddress);
const paths = [];
for (let i = 0; i < 6; i++) {
const shiftedSponsorAddressBN = sponsorAddressBN.shr(31 * i);
paths.push(shiftedSponsorAddressBN.mask(31).toString());
}
const AIRSEEKER_PROTOCOL_ID = '5'; // From: https://github.com/api3dao/airnode/blob/ef16c54f33d455a1794e7886242567fc47ee14ef/packages/airnode-protocol/src/index.ts#L46
return `${AIRSEEKER_PROTOCOL_ID}/${paths.join('/')}`;
}

const deriveSponsorWallet = (sponsorWalletMnemonic, dapiName) => {
// Take first 20 bytes of dapiName as sponsor address together with the "0x" prefix.
const sponsorAddress = ethers.utils.getAddress(dapiName.slice(0, 42));
const sponsorWallet = ethers.Wallet.fromMnemonic(
sponsorWalletMnemonic,
`m/44'/60'/0'/${deriveWalletPathFromSponsorAddress(sponsorAddress)}`
);

return sponsorWallet;
};

setInterval(async () => {
const provider = new ethers.providers.JsonRpcProvider(rpcUrl);
const dapiDataRegistry = new ethers.Contract(dapiDataRegistryAddress, dapiDataRegistryAbi, provider);
Expand Down Expand Up @@ -1580,7 +1603,7 @@ <h2>Active dAPIs</h2>

const deviationPercentage = calculateUpdateInPercentage(value, newBeaconSetValue).toNumber() / 1e6;
const deviationThresholdPercentage = deviationThresholdInPercentage.toNumber() / 1e6;

const sponsorWallet = deriveSponsorWallet(airseekerMnemonic, dapiName);
const dapiInfo = {
dapiName: dapiName,
decodedDapiName: ethers.utils.parseBytes32String(dapiName),
Expand All @@ -1592,11 +1615,13 @@ <h2>Active dAPIs</h2>
deviationPercentage:
deviationPercentage > deviationThresholdPercentage ? `<b>${deviationPercentage}</b>` : deviationPercentage,
deviationThresholdPercentage: deviationThresholdPercentage,
sponsorWalletAddress: sponsorWallet.address,
sponsorWalletBalance: ethers.utils.formatEther(await provider.getBalance(sponsorWallet.address)),
};

newActiveDapisHtml += JSON.stringify(dapiInfo, null, 2) + '\n\n';
}
document.getElementById('activeDapis').innerHTML = newActiveDapisHtml;
}, 1000);
}, 3000);
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Increased to avoid spamming public RPC providers too much.

</script>
</html>
Loading