Skip to content
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
91 changes: 48 additions & 43 deletions contracts/StateChannel.sol
Original file line number Diff line number Diff line change
Expand Up @@ -183,10 +183,12 @@ contract StateChannel is Initializable, OwnableUpgradeable, SQParameter {
require(channels[channelId].status == ChannelStatus.Finalized, 'SC001');

// check indexer registered
IIndexerRegistry indexerRegistry = IIndexerRegistry(
settings.getContractAddress(SQContracts.IndexerRegistry)
require(
IIndexerRegistry(settings.getContractAddress(SQContracts.IndexerRegistry)).isIndexer(
indexer
),
'G002'
);
require(indexerRegistry.isIndexer(indexer), 'G002');

// check sign
bytes32 payload = keccak256(
Expand All @@ -203,35 +205,31 @@ contract StateChannel is Initializable, OwnableUpgradeable, SQParameter {
);
if (_isContract(consumer)) {
require(consumer.supportsInterface(type(IConsumer).interfaceId), 'G018');
IConsumer cConsumer = IConsumer(consumer);
require(cConsumer.checkSign(channelId, payload, consumerSign), 'C006');
cConsumer.paid(channelId, msg.sender, amount, callback);
require(IConsumer(consumer).checkSign(channelId, payload, consumerSign), 'C006');
} else {
_checkSign(payload, consumerSign, consumer, false);
require(msg.sender == consumer, 'SC111');
}

_checkSign(payload, indexerSign, indexer, true);

// transfer the balance to contract
IERC20(settings.getContractAddress(SQContracts.SQToken)).safeTransferFrom(
consumer,
address(this),
amount
);

// initial the channel
ChannelState storage state = channels[channelId];
state.status = ChannelStatus.Open;
state.indexer = indexer;
state.consumer = consumer;
state.expiredAt = block.timestamp + expiration;
state.realTotal = amount;
state.total = amount;
// commented by @ian, now they will be set in _fundChannel()
// state.realTotal = real;
// state.total = amount;
state.spent = 0;
state.terminatedAt = 0;
state.deploymentId = deploymentId;
state.terminateByIndexer = false;

// transfer the rewards to channel
_fundChannel(channelId, deploymentId, consumer, amount, callback);

// set channel price
channelPrice[channelId] = price;

Expand Down Expand Up @@ -308,42 +306,16 @@ contract StateChannel is Initializable, OwnableUpgradeable, SQParameter {
bytes32 payload = keccak256(
abi.encode(channelId, indexer, consumer, preTotal, amount, callback)
);
address realConsumer = consumer;

// check sign
if (_isContract(consumer)) {
IConsumer cConsumer = IConsumer(consumer);
require(cConsumer.checkSign(channelId, payload, sign), 'C006');
realConsumer = cConsumer.channelConsumer(channelId);
require(IConsumer(consumer).checkSign(channelId, payload, sign), 'C006');
} else {
_checkSign(payload, sign, consumer, false);
}

// transfer the rewards to channel
address rbAddress = settings.getContractAddress(SQContracts.RewardsBooster);
uint256 rewardsAmount = IRewardsBooster(rbAddress).spendQueryRewards(
channels[channelId].deploymentId,
realConsumer,
amount,
abi.encode(channelId)
);

if (rewardsAmount < amount) {
// transfer the balance to contract
uint256 realAmount = amount - rewardsAmount;
if (_isContract(consumer)) {
IConsumer(consumer).paid(channelId, msg.sender, realAmount, callback);
}
IERC20(settings.getContractAddress(SQContracts.SQToken)).safeTransferFrom(
consumer,
address(this),
realAmount
);

channels[channelId].realTotal += realAmount;
}

channels[channelId].total += amount;
_fundChannel(channelId, channels[channelId].deploymentId, consumer, amount, callback);

emit ChannelFund(channelId, channels[channelId].realTotal, channels[channelId].total);
}
Expand Down Expand Up @@ -662,4 +634,37 @@ contract StateChannel is Initializable, OwnableUpgradeable, SQParameter {
}
return (size > 0);
}

function _fundChannel(
uint256 channelId,
bytes32 deploymentId,
address consumer,
uint256 amount,
bytes memory callback
) internal {
address realConsumer = consumer;
if (_isContract(consumer)) {
IConsumer cConsumer = IConsumer(consumer);
realConsumer = cConsumer.channelConsumer(channelId);
}
// transfer the rewards to channel
uint256 fundByReward = IRewardsBooster(
settings.getContractAddress(SQContracts.RewardsBooster)
).spendQueryRewards(deploymentId, realConsumer, amount, abi.encode(channelId));
uint256 realAmount = 0;
if (fundByReward < amount) {
realAmount = amount - fundByReward;
if (_isContract(consumer)) {
IConsumer(consumer).paid(channelId, msg.sender, realAmount, callback);
}
IERC20(settings.getContractAddress(SQContracts.SQToken)).safeTransferFrom(
consumer,
address(this),
realAmount
);
}

channels[channelId].realTotal += realAmount;
channels[channelId].total += amount;
}
}
8 changes: 7 additions & 1 deletion test/RewardsBooster.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -342,6 +342,7 @@ describe('RewardsBooster Contract', () => {
// spend query rewards
await token.connect(consumer0).increaseAllowance(stateChannel.address, etherParse('5'));

// open channel will consume query rewards now
await openChannel(
stateChannel,
defaultChannelId,
Expand All @@ -352,6 +353,9 @@ describe('RewardsBooster Contract', () => {
etherParse('1'),
60
);
const channel = await stateChannel.channel(defaultChannelId);
const consumedQueryReward = channel.total.sub(channel.realTotal);

const abi = ethers.utils.defaultAbiCoder;
const msg = abi.encode(
['uint256', 'address', 'address', 'uint256', 'uint256', 'bytes'],
Expand All @@ -366,7 +370,9 @@ describe('RewardsBooster Contract', () => {
const queryReward1 = await rewardsBooster.getQueryRewards(deploymentId3, consumer0.address);
const reward1 = await rewardsBooster.getAccRewardsForDeployment(deploymentId3);
// has at least 1 block's reward, not zero
expect(queryReward1).to.eq(getQueryReward(reward1.sub(reward0), queryRewardRatePerMill));
expect(queryReward1.add(consumedQueryReward)).to.eq(
getQueryReward(reward1.sub(reward0), queryRewardRatePerMill)
);
});

it('can add/remove booster - 1 booster account', async () => {
Expand Down
74 changes: 74 additions & 0 deletions test/StateChannel.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -299,6 +299,80 @@ describe('StateChannel Contract', () => {
});
});

describe('State Channel Rewards Open', () => {
const consumerInit = etherParse('10005');
beforeEach(async () => {
await registerRunner(token, indexerRegistry, staking, wallet_0, runner, etherParse('2000'));
await token.connect(wallet_0).transfer(treasury.address, etherParse('100000'));
await token.connect(wallet_0).transfer(consumer.address, consumerInit);
await token.connect(consumer).increaseAllowance(stateChannel.address, etherParse('5'));
await token.connect(wallet_0).transfer(rewardsBooster.address, etherParse('5'));

await boosterDeployment(token, rewardsBooster, consumer, deploymentId, etherParse('10000'));
});

it('open State Channel with booster rewards more than channel amount should work', async () => {
// 1000 blocks passed
await blockTravel(1000);
const queryRewardsBeforeCreating = await rewardsBooster.getQueryRewards(deploymentId, consumer.address);
// one block passed
await blockTravel(1);
const oneBlockRewards = (await rewardsBooster.getQueryRewards(deploymentId, consumer.address)).sub(
queryRewardsBeforeCreating
);
// one block passed
await openChannel(
stateChannel,
defaultChannelId,
deploymentId,
runner,
consumer,
etherParse('1'),
etherParse('1'),
time.duration.days(1).toString()
);

const queryRewardsAfterCreating = await rewardsBooster.getQueryRewards(deploymentId, consumer.address);

expect((await stateChannel.channel(defaultChannelId)).realTotal).to.equal(etherParse('0'));
expect((await stateChannel.channel(defaultChannelId)).total).to.equal(etherParse('1'));

expect(await token.balanceOf(consumer.address)).to.equal(etherParse('5'));
expect(queryRewardsAfterCreating as BigNumber).to.equal(
queryRewardsBeforeCreating.sub(etherParse('1')).add(oneBlockRewards.mul(2))
);
});

it('open State Channel with booster rewards less than channel amount should work', async () => {
// one block passed
await blockTravel(1);
const oneBlockRewards = await rewardsBooster.getQueryRewards(deploymentId, consumer.address);
// one block passed
await openChannel(
stateChannel,
defaultChannelId,
deploymentId,
runner,
consumer,
etherParse('1'),
etherParse('1'),
time.duration.days(1).toString()
);

const queryRewardsAfterCreating = await rewardsBooster.getQueryRewards(deploymentId, consumer.address);

expect((await stateChannel.channel(defaultChannelId)).realTotal).to.equal(
etherParse('1').sub(oneBlockRewards.mul(2))
);
expect((await stateChannel.channel(defaultChannelId)).total).to.equal(etherParse('1'));

expect(await token.balanceOf(consumer.address)).to.equal(
etherParse('5').sub(etherParse('1').sub(oneBlockRewards.mul(2)))
);
expect(queryRewardsAfterCreating as BigNumber).to.equal(etherParse('0'));
});
});

describe('State Channel Rewards Fund', () => {
const consumerInit = etherParse('10005');
beforeEach(async () => {
Expand Down
26 changes: 24 additions & 2 deletions test/StateChannelFlow.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { expect } from 'chai';
import { ethers, waffle } from 'hardhat';
import { deployContracts } from './setup';
import { deploymentIds } from './constants';
import { deploymentIds, deploymentMetadatas, projectMetadatas } from './constants';
import {
IndexerRegistry,
RewardsPool,
Expand All @@ -12,8 +12,10 @@ import {
StateChannel,
StakingManager,
RewardsHelper,
ProjectRegistry,
ProjectType,
} from '../src';
import { registerRunner, startNewEra, time, etherParse } from './helper';
import { registerRunner, startNewEra, time, etherParse, createProject } from './helper';
import { Wallet, BigNumber } from 'ethers';

describe('StateChannel Workflow Tests', () => {
Expand All @@ -23,6 +25,7 @@ describe('StateChannel Workflow Tests', () => {
let token: ERC20;
let staking: Staking;
let indexerRegistry: IndexerRegistry;
let projectRegistry: ProjectRegistry;
let eraManager: EraManager;
let rewardsDistributor: RewardsDistributor;
let rewardsPool: RewardsPool;
Expand Down Expand Up @@ -105,6 +108,7 @@ describe('StateChannel Workflow Tests', () => {
beforeEach(async () => {
const deployment = await waffle.loadFixture(deployer);
indexerRegistry = deployment.indexerRegistry;
projectRegistry = deployment.projectRegistry;
staking = deployment.staking;
token = deployment.token;
rewardsDistributor = deployment.rewardsDistributor;
Expand All @@ -126,6 +130,24 @@ describe('StateChannel Workflow Tests', () => {
await eraManager.connect(wallet_0).updateEraPeriod(time.duration.days(1).toString());
await startNewEra(eraManager);

// create projects
await createProject(
projectRegistry,
wallet_0,
projectMetadatas[0],
deploymentMetadatas[0],
deploymentIds[0],
ProjectType.SUBQUERY
);
await createProject(
projectRegistry,
wallet_0,
projectMetadatas[0],
deploymentMetadatas[0],
deploymentIds[1],
ProjectType.SUBQUERY
);

//create statechannels
channelId = ethers.utils.randomBytes(32);
channelId2 = ethers.utils.randomBytes(32);
Expand Down