Creating a Contract with PaymentSplitterUpgradeable

I am a novice at this, but from what I found, when initializing a transparent upgradeable contract, the contract's init() function is used to initialize instead of using the constructor() function. Using the wizard for an ERC20, for instance, you can click off the upgrade ability and some of the other optional features and see an initialize section for these. However, these init() functions are fairly simple whereas PaymentSplitterUpgradeable's initialize function needs two arrays.

What's an example of how to set that up with two wallets at 75 and 25 shares?

I have searched the internet for tutorials on how to use this upgradeable version of the contract, but no one has done anything to this specifically. This is frustrating, because I keep getting different errors depending on the way I attempt to initialize the __PaymentSplitter_init(...,...) function. There has to be a proper way to initialize it, right?

Also, because I can only initialize once, how do I upgrade that contract with new addresses and share values?

Show me you deployed a PaymentSplitterUpgradeable.sol imported contract, please. I have found zero examples.

I am currently on my laptop, when I get to my PC I will attempt to deploy this. out of curiosity did you try __PaymentSplitter_init_unchained(_payees, _shares) instead of __PaymentSplitter_init() ? might prevent the issues.

in terms of upgrading with different payees.. id have to test on my end to see what works and what doesn't

Check below link. May be it can help you.

Comeonline, this is not a deployment of the Upgradeable version. I don't see anything that could help me with my syntax problems.

BlockchainDev, I have not tried the unchained init function yet. None of the other contracts the wizard deploys uses an unchained unit function, so it seemed improper.

I appreciate any insight you may have if you could look at your PC.

This is the closest I have seen to a solution:

Which is a minimal proxy contract for:

No idea who made it but it could work for what I need... still no idea how to upgrade it.

Another example I found was Fraktal NFT. They abandoned their project, but the contracts are audited. Looks like they modified the original OpenZepellin contract to suit their needs:

Exact Match for Proxy Code:

... still I found another example... pretty terrible in terms of simplicity. "Split" on Polygon, same on Avalanche: Polygon: Avalanche:

Lastly, I found which made use of a custom "cloneable" variant on the original OZ contract. This is the ETH variant:

Charges a "tax", too, to clone.


All of these don't actually deploy a simple PaymentSplitterUpgradeable contract. They all twist the code in some way that I can't follow.

I got rid of the error and discovered a new problem... I was using Remix IDE to do something only Hardhat and Truffle can do. Having never set up either one before, I installed git, installed nodejs, installed Visual Studio, npm'd a bunch of stuff through the terminal like Hardhat and OZ libs, then configured Hardhat for Polygon and Mumbai using .env which used keys from Alchemy, Polygonscan, and MetaMask. So I think it is set up on my machine? Gd nothing to show for it though.

Coding the real way sucks. Everything should work like the wizard.

All this time, I thought people were using a fancier Remix IDE in their tutorials. I watch these on a phone screen, to my defense. When I found out half these or more were Visual Studio and that wasn't code for some fancy Remix, I felt pretty dumb and out of the loop. The "always the last to know" feeling was strong haha.

Anyhoo... this is what I did to remove the error:

Dynamic memory address arrays are an enigma it turns out, but passing these through an initialize function should work to bypass my syntax woes. I have no idea what to do next, because I think I just passed the problem on to future croc.

And yes this is an upgradeable token I am creating with a payment splitter. It's a social token for an NFT project, and the royalties are split with a charity whose address may change. I am avoiding the scenario where someone accidentally double pastes the token address into the send field. We can return our share, and the rest they can request from the charity. Probably will never happen, but I want Murphy's Law on my side.

Still having syntax issues...

Any help wold be much appreciated.


I put the addresses in quotes, and it worked.

is it allowed us to remove or update any payee's wallet addy after initialize?

I haven't tested that yet.

What I have done is verified the contract through hardhat and then performed transfers of MATIC and NotGAIA (yeah I sent it to itself). I also was able to release all from the token contract 4 calls and a single wallet. check out the transactions here:

In preparation for the next test round, I sent the same MATIC and NotGAIA. I did it so I'll be able to see what happens when I replace one wallet with another and switch the shares from 75/25 to 50/50 when there is already money owed.

Following the tutorial here: OpenZeppelin Upgrades: Step by Step Tutorial for Hardhat

I didn't see a way to pass the arguments for the initializer for another implementation, so I looked up the API:

I may be able to use "upgradeProxy" and "verify" separately instead of "prepareUpgrade", since prepareUpgrade has no calls that I can pass with the implementation contract.

I'm not confident it will work, however, since "PaymentSplitterUpgradeable" doesn't actually have any functions that remove payees, change shares, or clear data. Unfortunately, I think effectively calling the initialize function again will either fail or append the data. If that's the case, someone with some real coding aptitude should PR that crap and add true upgradeability.

Add some version of this while you're at it:

     * @dev Getter for the address of the payee number `index`.
    function payee(uint256 index) public view returns (address) {
        return _payees[index];

     * @dev Get the number of payees
    function payeeCount() public view returns (uint256) {
        return _payees.length;

     * @dev Release the owed amount of token to all of the payees.
    function distribute() public virtual {
        uint256 count = payeeCount();
        for (uint256 i = 0; i < count; i++) {
            // note: `_release` should not fail because payee always has shares, protected by `_appPay`

     * @dev Release owed amount of the `token` to all of the payees.
    function distribute(IERC20Upgradeable token) public virtual {
        uint256 count = payeeCount();
        for (uint256 i = 0; i < count; i++) {
            // note: `_release` should not fail because payee always has shares, protected by `_appPay`
            _release(token, payee(i));

Encourages gas token altruism. Stole it from the "Split" contract linked in a previous post.

Executing the call above was a bust as suspected. Cannot re-use initialize function.

I'll try the unchained version.

Using the unchained function was also a bust. I think it needs to be in the main contract level:

Looks like this is where my journey ends.

Turns out... not only is there no pre-written function to modify payee and shares_ arrays, they are also private, so I do not have write access to overwrite them by adding a new function. To write a custom PaymentSplitterActuallyUpgradeableTheWayWeWant.sol file would not be advisable since it modifies an audited library file, and I cannot afford to have something I hacked together audited.

I'm much better off going with a project like and using their updateSplit() function. Also audited, and I don't have to modify their functions to get it to do what I want.