2

I am a new developer, working on a smart contract mostly for my own learning. I am developing a Smart Contract to handle subscriptions. The contract has the below features.

  • Subscribers can subscribe to other public addresses and deposit ether into their account.
  • Producers (people you subscribe to) can execute a charge function, which will loop though all of their subscriptions, calculate the payout, deduct the payout from subscriber accounts and send the ether to the person calling the function.

Here is a snippet of my code:

function charge() external {
uint payout;
uint service;

Producer storage producer = producers[msg.sender];

// calculate payout
for (uint i = 0; i < producer.subscribers.length; i++) {
  // get subscriber 
  address subscriberAddress = producer.subscribers[i].account;
  Subscriber storage subscriber = subscribers[subscriberAddress];
  //check balance
  if (subscriber.balance >= weeklyCharge) {
    subscriber.balance -= weeklyCharge + serviceCharge;
    payout += weeklyCharge;
    service += serviceCharge;
  } 
}
// send total ETH to producer address
producer.lastPayment = now;
msg.sender.transfer(payout);
serviceAddress.transfer(serviceCharge);
Charged(msg.sender, payout, service);
} 

I am leaving out some details for simplicity's sake. The full code is here: https://github.com/cseale/patrecoin/blob/master/smart-contracts/contracts/Subscriptions.sol.

This charge function seems to me to potentially be a very expensive function, if someone had, let's say, 1M subscribers.

Can anyone advise me on how I might make this function more efficient, or design my smart contracts in a better way?

EDIT: One restriction. I want to put the control for charging into the hands of the person calling the charge function. If I invert the payment control to the subscribers, there is no point in the contract. I want to set up this accounting system to track payments and ensure a certain revenue flow for what I call the "Producer"

1 Answer 1

0

I know it's late, but what the hell.

What you're asking for would require a complete redesign on how the entire system works. I say this to highlight this because you ask a good question. I might be better to frame this as you're actually asking "How to avoid iteration in contract design".

This would be a broader exploration of software architecture to cover the entire concept. For now we can consider a few basic contract design best practices, at least potential candidates.

Distribute operations over transactions

This would involve doing all the calculations during the deposit to produce information that can used during the charge in a way to avoids iteration.

An example of this can be seen in many staking contracts. Where the amount deposited is added to a variable storing the total amount deposited. This way, the claim process can complete calculations based on the deposit for the user and the total amount deposited to calculate a percentage that can used to calculate how much can be claimed.

Don't iterate, calculate

This would be the follow up to the above point. When designing a workflow, don't consider iteration a viable solution at first. Attempt to design a workflow that can make a simple calculation based on prepared data. Once you determine what information you would need to complete the work with a simple calculation instead of iteration, work backwards to figure out how you can prepare the information you'll need for a non-iterative solution.

Iterate to aggregate

This principle highlights how iteration inside a contract should be saved for aggregating the information from the first two principles for a final calculation. This eliminates the potential for ON complexity antipatterns. And leads to the most efficient implementation possible. An example use case for this would be if you redesigned the subscription process to behave more like staking, and wanted to be able to handle multiple types of subscriptions that can payout to the same party in one transaction.

You can see examples of this in most multi pool staking contracts.

Defiat has an example of this in their AnyStake repo. Defiat AnyStake repo

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.