Skip to content

Commit ae1dea9

Browse files
Merge pull request #109 from argentlabs/revert-95-feature/token-provider-remove-kyber
Revert "Remove Kyber price syncing from TokenPriceProvider"
2 parents d536ea2 + 6abd414 commit ae1dea9

File tree

11 files changed

+67
-36
lines changed

11 files changed

+67
-36
lines changed

contracts/infrastructure/TokenPriceProvider.sol

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,24 @@ pragma solidity ^0.5.4;
1717
import "../../lib/utils/SafeMath.sol";
1818
import "../../lib/other/ERC20.sol";
1919
import "../base/Managed.sol";
20+
import "../../lib/other/KyberNetwork.sol";
2021

2122
contract TokenPriceProvider is Managed {
23+
24+
// Mock token address for ETH
25+
address constant internal ETH_TOKEN_ADDRESS = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
26+
2227
using SafeMath for uint256;
2328

2429
mapping(address => uint256) public cachedPrices;
2530

31+
// Address of the KyberNetwork contract
32+
KyberNetwork public kyberNetwork;
33+
34+
constructor(KyberNetwork _kyberNetwork) public {
35+
kyberNetwork = _kyberNetwork;
36+
}
37+
2638
function setPrice(ERC20 _token, uint256 _price) public onlyManager {
2739
cachedPrices[address(_token)] = _price;
2840
}
@@ -44,4 +56,26 @@ contract TokenPriceProvider is Managed {
4456
uint256 price = cachedPrices[_token];
4557
return price.mul(_amount).div(10**decimals);
4658
}
59+
60+
//
61+
// The following is added to be backward-compatible with Argent's old backend
62+
//
63+
64+
function setKyberNetwork(KyberNetwork _kyberNetwork) external onlyManager {
65+
kyberNetwork = _kyberNetwork;
66+
}
67+
68+
function syncPrice(ERC20 _token) external {
69+
require(address(kyberNetwork) != address(0), "Kyber sync is disabled");
70+
(uint256 expectedRate,) = kyberNetwork.getExpectedRate(_token, ERC20(ETH_TOKEN_ADDRESS), 10000);
71+
cachedPrices[address(_token)] = expectedRate;
72+
}
73+
74+
function syncPriceForTokenList(ERC20[] calldata _tokens) external {
75+
require(address(kyberNetwork) != address(0), "Kyber sync is disabled");
76+
for (uint16 i = 0; i < _tokens.length; i++) {
77+
(uint256 expectedRate,) = kyberNetwork.getExpectedRate(_tokens[i], ERC20(ETH_TOKEN_ADDRESS), 10000);
78+
cachedPrices[address(_tokens[i])] = expectedRate;
79+
}
80+
}
4781
}

deployment/2_deploy_contracts.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ const deploy = async (network) => {
4343
// Deploy the MultiSig
4444
const MultiSigWrapper = await deployer.deploy(MultiSig, {}, newConfig.multisig.threshold, newConfig.multisig.owners);
4545
// Deploy TokenPriceProvider
46-
const TokenPriceProviderWrapper = await deployer.deploy(TokenPriceProvider);
46+
const TokenPriceProviderWrapper = await deployer.deploy(TokenPriceProvider, {}, newConfig.Kyber.contract);
4747
// Deploy Module Registry
4848
const ModuleRegistryWrapper = await deployer.deploy(ModuleRegistry);
4949
// Deploy Compound Registry

deployment/7_upgrade_1_6.js

Lines changed: 1 addition & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -63,9 +63,7 @@ const deploy = async (network) => {
6363
await MakerRegistryWrapper.verboseWaitForTransaction(addCollateralTransaction, `Adding join adapter ${wethJoinAddress} to the MakerRegistry`);
6464
const changeMakerRegistryOwnerTx = await MakerRegistryWrapper.contract.changeOwner(config.contracts.MultiSigWallet, { gasPrice });
6565
await MakerRegistryWrapper.verboseWaitForTransaction(changeMakerRegistryOwnerTx, "Set the MultiSig as the owner of the MakerRegistry");
66-
67-
// Deploy new TokenPriceProvider instance
68-
const TokenPriceProviderWrapper = await deployer.deploy(TokenPriceProvider);
66+
const TokenPriceProviderWrapper = await deployer.wrapDeployedContract(TokenPriceProvider, config.contracts.TokenPriceProvider);
6967

7068
// //////////////////////////////////
7169
// Deploy new modules
@@ -131,7 +129,6 @@ const deploy = async (network) => {
131129

132130
configurator.updateInfrastructureAddresses({
133131
MakerRegistry: MakerRegistryWrapper.contractAddress,
134-
TokenPriceProvider: TokenPriceProviderWrapper.contractAddress,
135132
});
136133

137134
const gitHash = childProcess.execSync("git rev-parse HEAD").toString("utf8").replace(/\n$/, "");
@@ -144,7 +141,6 @@ const deploy = async (network) => {
144141
abiUploader.upload(MakerV2ManagerWrapper, "modules"),
145142
abiUploader.upload(TransferManagerWrapper, "modules"),
146143
abiUploader.upload(MakerRegistryWrapper, "contracts"),
147-
abiUploader.upload(TokenPriceProviderWrapper, "contracts"),
148144
]);
149145

150146
// //////////////////////////////////
@@ -157,20 +153,6 @@ const deploy = async (network) => {
157153
[wrapper.contractAddress, utils.asciiToBytes32(wrapper._contract.contractName)]);
158154
}
159155

160-
// ////////////////////////////////////////////////////
161-
// Change the owner of TokenPriceProvider
162-
// ////////////////////////////////////////////////////
163-
164-
for (const idx in config.backend.accounts) {
165-
const account = config.backend.accounts[idx];
166-
const TokenPriceProviderAddManagerTx = await TokenPriceProviderWrapper.contract.addManager(account, { gasPrice });
167-
await TokenPriceProviderWrapper.verboseWaitForTransaction(TokenPriceProviderAddManagerTx,
168-
`Set ${account} as the manager of the TokenPriceProvider`);
169-
}
170-
171-
const changeOwnerTx = await TokenPriceProviderWrapper.contract.changeOwner(config.contracts.MultiSigWallet, { gasPrice });
172-
await TokenPriceProviderWrapper.verboseWaitForTransaction(changeOwnerTx, "Set the MultiSig as the owner of TokenPriceProvider");
173-
174156
// //////////////////////////////////
175157
// Deploy and Register upgraders
176158
// //////////////////////////////////

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
"test": "npx etherlime test --skip-compilation",
1515
"ctest": "npm run compile && npm run test",
1616
"provision:lib:artefacts": "bash ./scripts/provision_lib_artefacts.sh",
17-
"test:coverage": "bash ./scripts/provision_lib_artefacts.sh & COVERAGE=1 npx etherlime coverage --solcVersion 0.5.4 && istanbul check-coverage --statements 71 --branches 62 --functions 73 --lines 71",
17+
"test:coverage": "bash ./scripts/provision_lib_artefacts.sh & COVERAGE=1 npx etherlime coverage --solcVersion 0.5.4 && istanbul check-coverage --statements 71 --branches 61 --functions 73 --lines 71",
1818
"lint:contracts": "npx ethlint --dir .",
1919
"lint:contracts:staged": "bash ./scripts/ethlint.sh",
2020
"test:deployment": "./scripts/deploy.sh ganache `seq 1 7`",

test/approvedTransfer.js

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ const Registry = require("../build/ModuleRegistry");
66
const GuardianStorage = require("../build/GuardianStorage");
77
const GuardianManager = require("../build/GuardianManager");
88
const ApprovedTransfer = require("../build/ApprovedTransfer");
9+
const KyberNetwork = require("../build/KyberNetworkTest");
910
const TokenPriceProvider = require("../build/TokenPriceProvider");
1011
const ERC20 = require("../build/TestERC20");
1112
const TestContract = require("../build/TestContract");
@@ -14,7 +15,7 @@ const TestManager = require("../utils/test-manager");
1415
const { sortWalletByAddress, parseRelayReceipt, ETH_TOKEN } = require("../utils/utilities.js");
1516

1617
const DECIMALS = 12; // number of decimal for TOKN contract
17-
const TOKEN_RATE = 51 * 10 ** 13; // 1 TOKN = 0.00051 ETH
18+
const KYBER_RATE = 51 * 10 ** 13; // 1 TOKN = 0.00051 ETH
1819

1920
const ZERO_BYTES32 = ethers.constants.HashZero;
2021

@@ -38,24 +39,26 @@ describe("Approved Transfer", function () {
3839
let guardianManager;
3940
let approvedTransfer;
4041
let priceProvider;
42+
let kyber;
4143
let erc20;
4244
const amountToTransfer = 10000;
4345

4446
before(async () => {
4547
deployer = manager.newDeployer();
4648
const registry = await deployer.deploy(Registry);
4749
const guardianStorage = await deployer.deploy(GuardianStorage);
48-
priceProvider = await deployer.deploy(TokenPriceProvider);
50+
kyber = await deployer.deploy(KyberNetwork);
51+
priceProvider = await deployer.deploy(TokenPriceProvider, {}, kyber.contractAddress);
4952
await priceProvider.addManager(infrastructure.address);
5053
guardianManager = await deployer.deploy(GuardianManager, {}, registry.contractAddress, guardianStorage.contractAddress, 24, 12);
5154
approvedTransfer = await deployer.deploy(ApprovedTransfer, {}, registry.contractAddress, guardianStorage.contractAddress);
5255
});
53-
5456
beforeEach(async () => {
5557
wallet = await deployer.deploy(Wallet);
5658
await wallet.init(owner.address, [approvedTransfer.contractAddress, guardianManager.contractAddress]);
5759
erc20 = await deployer.deploy(ERC20, {}, [infrastructure.address, wallet.contractAddress], 10000000, DECIMALS); // TOKN contract with 10M tokens (5M TOKN for wallet and 5M TOKN for account[0])
58-
await priceProvider.setPrice(erc20.contractAddress, TOKEN_RATE);
60+
await kyber.addToken(erc20.contractAddress, KYBER_RATE, DECIMALS);
61+
await priceProvider.syncPrice(erc20.contractAddress);
5962
await infrastructure.sendTransaction({ to: wallet.contractAddress, value: 50000000 });
6063
});
6164

test/makerV2Manager_loan.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ describe("MakerV2 Vaults", function () {
100100
);
101101

102102
// Deploy TransferManager
103-
const priceProvider = await deployer.deploy(TokenPriceProvider);
103+
const priceProvider = await deployer.deploy(TokenPriceProvider, {}, AddressZero);
104104
const transferStorage = await deployer.deploy(TransferStorage);
105105
transferManager = await deployer.deploy(TransferManager, {},
106106
AddressZero,

test/tokenExchanger.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ const ERC20 = require("../build/TestERC20");
1111
const { ETH_TOKEN } = require("../utils/utilities.js");
1212

1313
const DECIMALS = 12; // number of decimal for TOKN contract
14-
const TOKEN_RATE = 51 * 10 ** 13; // 1 TOKN = 0.00051 ETH
14+
const KYBER_RATE = 51 * 10 ** 13; // 1 TOKN = 0.00051 ETH
1515
const FEE_RATIO = 30;
1616

1717
const TestManager = require("../utils/test-manager");
@@ -43,7 +43,7 @@ describe("Token Exchanger", function () {
4343
wallet = await deployer.deploy(Wallet);
4444
await wallet.init(owner.address, [exchanger.contractAddress]);
4545
erc20 = await deployer.deploy(ERC20, {}, [kyber.contractAddress, wallet.contractAddress], 10000000, DECIMALS); // TOKN contract with 10M tokens (5M TOKN for wallet and 5M TOKN for kyber)
46-
await kyber.addToken(erc20.contractAddress, TOKEN_RATE, DECIMALS);
46+
await kyber.addToken(erc20.contractAddress, KYBER_RATE, DECIMALS);
4747
await infrastructure.sendTransaction({ to: wallet.contractAddress, value: 50000000 });
4848
await infrastructure.sendTransaction({ to: kyber.contractAddress, value: 50000000 });
4949
});

test/tokenPriceProvider.js

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
/* global accounts */
22

33
const ERC20 = require("../build/TestERC20");
4+
const KyberNetwork = require("../build/KyberNetworkTest");
45
const TokenPriceProvider = require("../build/TokenPriceProvider");
56

67
const TestManager = require("../utils/test-manager");
@@ -12,21 +13,30 @@ describe("Token Price Provider", () => {
1213
const priceProviderManager = accounts[1].signer;
1314

1415
let deployer;
16+
let kyber;
1517
let priceProvider;
1618
let erc20First;
1719
let erc20Second;
1820

1921
before(async () => {
2022
deployer = manager.newDeployer();
23+
kyber = await deployer.deploy(KyberNetwork);
2124
});
2225

2326
beforeEach(async () => {
24-
priceProvider = await deployer.deploy(TokenPriceProvider);
27+
priceProvider = await deployer.deploy(TokenPriceProvider, {}, kyber.contractAddress);
2528
await priceProvider.addManager(priceProviderManager.address);
2629
erc20First = await deployer.deploy(ERC20, {}, [infrastructure.address], 10000000, 18);
2730
erc20Second = await deployer.deploy(ERC20, {}, [infrastructure.address], 10000000, 18);
2831
});
2932

33+
it("should be able to set the Kyber network contract", async () => {
34+
const kyberNew = await deployer.deploy(KyberNetwork);
35+
await priceProvider.from(priceProviderManager).setKyberNetwork(kyberNew.contractAddress);
36+
const kyberContract = await priceProvider.kyberNetwork();
37+
assert.equal(kyberNew.contractAddress, kyberContract);
38+
});
39+
3040
describe("Reading and writing token prices", () => {
3141
it("should set token price correctly", async () => {
3242
await priceProvider.from(priceProviderManager).setPrice(erc20First.contractAddress, 1800);

test/transferManager.js

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ const TransferStorage = require("../build/TransferStorage");
77
const GuardianStorage = require("../build/GuardianStorage");
88
const TransferModule = require("../build/TransferManager");
99
const LegacyTransferManager = require("../build/LegacyTransferManager");
10+
const KyberNetwork = require("../build/KyberNetworkTest");
1011
const TokenPriceProvider = require("../build/TokenPriceProvider");
1112
const ERC20 = require("../build/TestERC20");
1213
const TestContract = require("../build/TestContract");
@@ -17,7 +18,7 @@ const ETH_LIMIT = 1000000;
1718
const SECURITY_PERIOD = 2;
1819
const SECURITY_WINDOW = 2;
1920
const DECIMALS = 12; // number of decimal for TOKN contract
20-
const TOKEN_RATE = ethers.utils.bigNumberify(51 * 10 ** 13); // 1 TOKN = 0.00051 ETH
21+
const KYBER_RATE = ethers.utils.bigNumberify(51 * 10 ** 13); // 1 TOKN = 0.00051 ETH
2122
const ZERO_BYTES32 = ethers.constants.HashZero;
2223

2324
const ACTION_TRANSFER = 0;
@@ -36,6 +37,7 @@ describe("TransferManager", function () {
3637
const spender = accounts[4].signer;
3738

3839
let deployer;
40+
let kyber;
3941
let registry;
4042
let priceProvider;
4143
let transferStorage;
@@ -48,9 +50,8 @@ describe("TransferManager", function () {
4850
before(async () => {
4951
deployer = manager.newDeployer();
5052
registry = await deployer.deploy(Registry);
51-
priceProvider = await deployer.deploy(TokenPriceProvider);
52-
await priceProvider.addManager(infrastructure.address);
53-
53+
kyber = await deployer.deploy(KyberNetwork);
54+
priceProvider = await deployer.deploy(TokenPriceProvider, {}, kyber.contractAddress);
5455
transferStorage = await deployer.deploy(TransferStorage);
5556
guardianStorage = await deployer.deploy(GuardianStorage);
5657

@@ -81,7 +82,8 @@ describe("TransferManager", function () {
8182
wallet = await deployer.deploy(Wallet);
8283
await wallet.init(owner.address, [transferModule.contractAddress]);
8384
erc20 = await deployer.deploy(ERC20, {}, [infrastructure.address, wallet.contractAddress], 10000000, DECIMALS); // TOKN contract with 10M tokens (5M TOKN for wallet and 5M TOKN for account[0])
84-
await priceProvider.setPrice(erc20.contractAddress, TOKEN_RATE);
85+
await kyber.addToken(erc20.contractAddress, KYBER_RATE, DECIMALS);
86+
await priceProvider.syncPrice(erc20.contractAddress);
8587
await infrastructure.sendTransaction({ to: wallet.contractAddress, value: ethers.utils.bigNumberify("1000000000000000000") });
8688
});
8789

utils/config/ganache.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
{"ENS":{"deployOwnRegistry":true,"ensRegistry":"0x9d6CfEc16741f08991654Bc36d1045A1D241F29e","domain":"argent.xyz"},"backend":{"accounts":["0xD9995BAE12FEe327256FFec1e3184d492bD94C31"]},"multisig":{"owners":["0xD9995BAE12FEe327256FFec1e3184d492bD94C31"],"threshold":1,"autosign":true},"settings":{"deployer":{"type":"ganache"},"lockPeriod":480,"recoveryPeriod":480,"securityPeriod":240,"securityWindow":240,"feeRatio":15,"defaultLimit":"1000000000000000000"},"Kyber":{"deployOwn":true,"contract":"0x81A1198d28a7491F1257B22DaB444a73ABCf9298"},"CryptoKitties":{"contract":"0x0000000000000000000000000000000000000000"},"defi":{"maker":{"deployOwn":true,"tub":"0x0000000000000000000000000000000000000000","pot":"0x0000000000000000000000000000000000000000","jug":"0x0000000000000000000000000000000000000000","migration":"0x09ce60Bd50333a737e03db957bd7f845b08DB095"},"uniswap":{"deployOwn":true,"factory":"0xb5d7Ef06dd742b62d71eb2CaBa47d320b4D2A578"},"compound":{"comptroller":"0x0000000000000000000000000000000000000000","markets":{"0x0000000000000000000000000000000000000000":"0x0000000000000000000000000000000000000000"}}},"contracts":{"MultiSigWallet":"0xE6192099D3489211a92be2645aDe339ad4998a8c","WalletFactory":"0x0d326c46DCb8a85242C80d3c2A1a426CD2328401","ENSResolver":"0xD629AF06612B631c5b370055dA1cB3FB46A1c392","ENSManager":"0xb7358663Ed8f0E0aF7b05482AF9E8016DE3b554F","TokenPriceProvider":"0x1C2878ABd6a189D5449e797D735545A9C08d9234","ModuleRegistry":"0x0c1bCf5DF51D47ebD980cBb870dF35dCE283e771","BaseWallet":"0xa9442f66E6bEF32c3d196B71e943406e4722326b","CompoundRegistry":"0x93e33b71aF68a10ACd863A5DF1D5D81719Ac15B2","MakerRegistry":"0x4576CA92833f8A1E99A33E1595cce9E5349dD19d"},"modules":{"GuardianStorage":"0xE9c77f070a5671fA32381ac826036eE3FDF5Ad56","TransferStorage":"0x73C62c291621e20799CA5D6Bf7b114F678ebE9Cb","GuardianManager":"0xd85C69EA74b1DBaBb40Eea240E5Cd2Ce4426856F","LockManager":"0x0eA33b7525189c49e8Da46AD99278480811fcA64","RecoveryManager":"0xC07f212AaEeF5Dc948BBC5302cB46D9D799417Ec","ApprovedTransfer":"0x84c01919c85CE8c0a43F7A6b95FC5B3cf6230D0a","TokenExchanger":"0x88A8e4a07fed276B6EeDB33449bfE9b0b5448B1F","NftTransfer":"0xD41288d45765e93ef63F58ac11CAa271BdA2bC21","MakerManager":"0x8A90941A8fB0B386eEEbFb2E1E96b54Af59ec29B","TransferManager":"0x39dCCe15d1a2c17c61B757C34cbe6aacF813Edc1","CompoundManager":"0xDCD324973848077F310E884CA7207b1Ecc67B493","MakerV2Manager":"0xFc31F3f732e9F1293ca3eb587871691bC8b28b82"},"gitCommit":"be87cf3131b90214644372a9331d20e64622ce03"}
1+
{"ENS":{"deployOwnRegistry":true,"ensRegistry":"0x274C3fB4B9A3f5836612C169ADEE957be8E1CA49","domain":"argent.xyz"},"backend":{"accounts":["0xD9995BAE12FEe327256FFec1e3184d492bD94C31"]},"multisig":{"owners":["0xD9995BAE12FEe327256FFec1e3184d492bD94C31"],"threshold":1,"autosign":true},"settings":{"deployer":{"type":"ganache"},"lockPeriod":480,"recoveryPeriod":480,"securityPeriod":240,"securityWindow":240,"feeRatio":15,"defaultLimit":"1000000000000000000"},"Kyber":{"deployOwn":true,"contract":"0xCE7A2788C4f14A3fe43c9d66eAe5C5b8ffd40240"},"CryptoKitties":{"contract":"0x0000000000000000000000000000000000000000"},"defi":{"maker":{"deployOwn":true,"tub":"0x0000000000000000000000000000000000000000","pot":"0x0000000000000000000000000000000000000000","jug":"0x0000000000000000000000000000000000000000","migration":"0xD2877b6409313680d2f7186f11CB584961135A62"},"uniswap":{"deployOwn":true,"factory":"0xB2533a6e92F0d638302aB6808FE3Ba6023cFEE08"},"compound":{"comptroller":"0x0000000000000000000000000000000000000000","markets":{"0x0000000000000000000000000000000000000000":"0x0000000000000000000000000000000000000000"}}},"contracts":{"MultiSigWallet":"0x118b5e4FD0aF29800CFeEC3B2E037fA7172a5A1C","WalletFactory":"0xe22c9dE7D0c258Fe64BcA9B19AF6ad9557C0a405","ENSResolver":"0xEf80C55AF72e8df28daed66B71137C7532B5c8Ea","ENSManager":"0x45a2Fe7F67a58fAEcc6109aB99304d7009B4D7F5","TokenPriceProvider":"0xdDD61b72d8019FEA5459ebD47d6bAc243e514fA2","ModuleRegistry":"0xf4DF8fe8c2b3882E9f5cC88008eE98e225c9753C","BaseWallet":"0x7A0E7cCae904066DD64968aE5fC857413D293e9d","CompoundRegistry":"0xeFf03c08398f4E744b365507D95355305B82ceab","MakerRegistry":"0x355d26E5f04255F60E910F1aA1251DE5D10996E8"},"modules":{"GuardianStorage":"0x5945751727ae5923AA39dC8Bd6a658925C53d300","TransferStorage":"0xebaf86d5dDdb586E19065e2D29dD5Ad9a31a8F31","GuardianManager":"0x2202723Bb717f362f64b6e41086A2cf52d9Ee28f","LockManager":"0xc1DA53758396f84198e78f6318904C2E666fC4Ea","RecoveryManager":"0xD6aC219C2b10a85D565ef8911e075464B5D53807","ApprovedTransfer":"0x11d3467345Ed6Ab494D131d42A45e469b2F7eB51","TokenExchanger":"0xA64fEeD186483380ad10496C4C9fADd89494633C","NftTransfer":"0x85D24B941A22D5DA687c607769173F01Abc61A56","MakerManager":"0x2718cAce241213B18F67C6d66B245EE77DB0Ce2a","TransferManager":"0xB58188A994fD291e2F97644b7FDb87372D27b2fD","CompoundManager":"0x1362D309a42165bC6dC00Be56D32625dF2C2c517","MakerV2Manager":"0x43f082E392cF13492a2660f932eFe22504Ab7517"},"gitCommit":"301c40ae9091b66f4faa30bbc5d20ae735febd3f"}

0 commit comments

Comments
 (0)